这种比较不能模棱两可的原因是既不是普通类型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; // error
失败,因为编译器无法basic_string<charT, traits, alloc>
从推断my_string
,尽管确实存在对其实例化的隐式转换。
然而,比较s1 == s2
确实有效,因为标准库的实现预计会提供可以考虑从任何类型到隐式转换的重载std::basic_string_view
(这种隐式转换存在于std::string
to 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;
通过将其中一个操作数的类型__identity
定义为template <class T> using __identity = decay_t<T>;
,它引入了一个非推导的 contextstd::basic_string_view
,为一些和另一个可隐式转换为std::basic_string_view
类模板的相同实例化的参数创建重载。