Comparison for objects derived from std::string_view is ambiguous in MSVC

旧巷老猫 提交于 2019-12-05 07:50:46

Yes you should expect your code to work; template argument deduction can deduce the base class in function calls, see [temp.deduct.call]/4.3

— If P is a class and P has the form simple-template-id, then the transformed A can be a derived class of the deduced A.

The issue with VS 2017 (15.3) is - the standard also has provisions for situations when one of the arguments is implicitly-convertible to std::string_view, see [string.view.comparison]:

Let S be basic_­string_­view<charT, traits>, and sv be an instance of S. Implementations shall provide sufficient additional overloads marked constexpr and noexcept so that an object t with an implicit conversion to S can be compared according to Table 67.

Table 67 — Additional basic_­string_­view comparison overloads

  • Expression t == sv equivalent to: S(t) == sv
  • Expression sv == t equivalent to: sv == S(t)
  • . . .

[ Example: A sample conforming implementation for operator== would be:

template<class T> using __identity = decay_t<T>;
template<class charT, class traits>
  constexpr bool operator==(basic_string_view<charT, traits> lhs,
                            basic_string_view<charT, traits> rhs) noexcept {
    return lhs.compare(rhs) == 0;
  }
template<class charT, class traits>
  constexpr bool operator==(basic_string_view<charT, traits> lhs,
                            __identity<basic_string_view<charT, traits>> rhs) noexcept {
    return lhs.compare(rhs) == 0;
  }
template<class charT, class traits>
  constexpr bool operator==(__identity<basic_string_view<charT, traits>> lhs,
                            basic_string_view<charT, traits> rhs) noexcept {
    return lhs.compare(rhs) == 0;
  }

— end example ]

This causes a problem in VS 2017 (15.3) because:

  • The MSVC compiler can't handle partial ordering of function templates w.r.t. non-deduced context (thanks @T.C.), so the implementation mentioned in the standard is not possible

  • Consequently, the MSVC standard library works around that with SFINAE for overloads #2 and #3, see xstring:

template<class _Elem,
  class _Traits,
  class _Conv, // TRANSITION, VSO#265216
  class = enable_if_t<is_convertible<_Conv, basic_string_view<_Elem, _Traits>>::value>>
  _CONSTEXPR14 bool operator==(_Conv&& _Lhs, const basic_string_view<_Elem, _Traits> _Rhs)
      _NOEXCEPT_OP(_NOEXCEPT_OP((basic_string_view<_Elem, _Traits>(_STD forward<_Conv>(_Lhs)))))
  {   // compare objects convertible to basic_string_view instances for equality
  return (_Rhs._Equal(_STD forward<_Conv>(_Lhs)));
  }

template<class _Elem,
  class _Traits,
  class _Conv, // TRANSITION, VSO#265216
  class = enable_if_t<is_convertible<_Conv, basic_string_view<_Elem, _Traits>>::value>>
  _CONSTEXPR14 bool operator==(const basic_string_view<_Elem, _Traits> _Lhs, _Conv&& _Rhs)
      _NOEXCEPT_OP(_NOEXCEPT_OP((basic_string_view<_Elem, _Traits>(_STD forward<_Conv>(_Rhs)))))
  {   // compare objects convertible to basic_string_view instances for equality
  return (_Lhs._Equal(_STD forward<_Conv>(_Rhs)));
  }

Unfortunately this is not the same as what was meant in the standard - since the signature of these overloads differs from the original one, and Foo&& is a better match than std::string_view (again thanks @T.C.), no partial ordering between #1, #2 and #3 is performed - overload resolution selects #2 and #3 as better candidates. Now these two are truly ambiguous - both are viable yet neither is more specialized.

As a workaround you can implement comparators for your types, or just a generic comparator for when both sides are convertible to string_view:

#include <string_view>

template<class T, class T2,
  class = std::enable_if_t<std::is_convertible<T, std::string_view>::value>,
  class = std::enable_if_t<std::is_convertible<T2, std::string_view>::value>>
constexpr bool operator==(T&& lhs, T2&& rhs) noexcept
{
  return lhs.compare(std::forward<T2>(rhs));
}

struct Foo : std::string_view {};

int main() {
  Foo f1{};
  Foo f2{};
  return f1 == f2;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!