14

我看到了两者std::string_viewstd::string具有对称性operator==(),并且std::string它具有接受的构造函数std::string_view和将自身转换为std::string_view. 因此,当我们尝试使用andoperator==()之间的比较时,它应该是模棱两可的吗?std::string_viewstd::string

我想我的想法一定有问题。谁能澄清一下?

例子:

std::string s1 = "123";
std::string_view s2 = "123";
// in the following comparison, will s1 use the convert operator to generate a string_view, or will s2 use string's string_view constructor to generate a string?
if (s1 == s2) {...}
4

2 回答 2

14

这种比较不能模棱两可的原因是既不是普通类型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不能是intchar,尽管两者之间存在转换。还:

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::stringto 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类模板的相同实例化的参数创建重载。

于 2018-09-12T16:54:36.270 回答
6

这是因为[string.view.comparisons]中有一个奇怪的子句:

S成为basic_­string_­view<charT, traits>,并sv成为 的一个实例S。实现应提供足够的附加重载标记constexprnoexcept以便可以根据表 62 比较t具有隐式转换的对象。S

表 62 列出了所有的比较运算符,以及表达式两侧的视图。

由于std::string隐式转换为std::string_view,因此将选择此重载。这样的重载将与s1 == s2大小写完全匹配,因此不会考虑隐式转换。

基本上,这是通过 SFINAE 工具实现的。像这样的东西:

template<typename Str>
std::enable_if_t<std::is_convertible_v<std::string_view, Str>, bool> operator==(const Str &rhs, const std::string_view &lhs);

这样的重载不需要隐式转换,所以它比任何重载都好。

于 2018-09-12T17:09:31.370 回答