这样的比较之所以不会产生歧义,是因为
std :: string
和
std :: string_view
都不是普通类型。相反,它们都是类模板实例化,并且各自具有比较运算符:
template <class charT, class traits, class alloc>
constexpr bool operator==(const basic_string<charT, traits, alloc>& lhs,
const basic_string<charT, traits, alloc>& rhs) noexcept;
template <class charT, class traits>
constexpr bool operator==(basic_string_view<charT, traits> lhs,
basic_string_view<charT, traits> rhs) noexcept;
这样定义的函数模板不考虑任何转换。相反,它们期望操作数完全相同类型,只有这样才能成功推导(左右操作数的模板参数可以推导出相同类型),从而产生一个可行的候选项。同样地:
template <typename T>
void foo(T, T);
foo(42, 'x'); // error
由于参数类型不匹配而导致失败,因为T
既不能是int
也不能是char
,尽管两者之间存在转换。此外:
struct my_string
{
operator std::string() const { return ""; }
};
std::string s;
my_string ms;
s == ms;
使用my_string
无法推断出basic_string<charT, traits, alloc>
的类型,因此会出现错误。但是s1 == s2
可以工作,因为标准库的实现预计提供重载函数来考虑从任何类型到std::basic_string_view
的隐式转换(例如从std::string
到std::string_view
的隐式转换)。可以通过抑制其中一个参数的推导来实现这一点,示例部分在[string.view.comparison]/p1中展示了如何实现。
template <class charT, class traits>
constexpr bool operator==(basic_string_view<charT, traits> lhs,
__identity<basic_string_view<charT, traits>> rhs) noexcept;
通过将其中一个操作数的类型定义为
template <class T> using __identity = decay_t<T>;
的
__identity
,它引入了一个
非推导上下文,为某些
std::basic_string_view
和另一个隐式转换为相同实例化的
std::basic_string_view
类模板的参数创建了一个重载。请注意保留HTML标签。
string_view
又有string
,那么op==
会使用哪一个。 - Lightness Races in Orbit