在比较中使用用户定义的转换和隐式转换

using user-defined conversions with implicit conversions in comparisons

本文关键字:转换 定义 用户 比较      更新时间:2023-10-16

我正在努力理解为什么以下代码不允许发生隐式转换。

#include <string>
using namespace std;
struct HasConversionToString {
  HasConversionToString(const string& s_) : s{s_} {}
  string s;
  operator const string&() const { return s; }
};
int main() {
  string s{"a"};
  HasConversionToString obj{"b"};
  return s < obj;
}

clang 和 gcc 都未能找到一种有效的方法来比较两个对象,

错误如下:
clang++ -std=c++14 -Wall -Wextra -pedantic conversion.cpp -o test
conversion.cpp:13:12: error: invalid operands to binary expression ('string' (aka 'basic_string<char>') and 'HasConversionToString')
  return s < obj;
         ~ ^ ~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/stl_pair.h:220:5: note: candidate template ignored: could not match
      'pair' against 'basic_string'
    operator<(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/stl_iterator.h:298:5: note: candidate template ignored: could not match
      'reverse_iterator' against 'basic_string'
    operator<(const reverse_iterator<_Iterator>& __x,
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/stl_iterator.h:348:5: note: candidate template ignored: could not match
      'reverse_iterator' against 'basic_string'
    operator<(const reverse_iterator<_IteratorL>& __x,
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/stl_iterator.h:849:5: note: candidate template ignored: could not match
      '__normal_iterator' against 'basic_string'
    operator<(const __normal_iterator<_IteratorL, _Container>& __lhs,
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/stl_iterator.h:856:5: note: candidate template ignored: could not match
      '__normal_iterator' against 'basic_string'
    operator<(const __normal_iterator<_Iterator, _Container>& __lhs,
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/stl_iterator.h:1089:5: note: candidate template ignored: could not match
      'move_iterator' against 'basic_string'
    operator<(const move_iterator<_IteratorL>& __x,
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/stl_iterator.h:1095:5: note: candidate template ignored: could not match
      'move_iterator' against 'basic_string'
    operator<(const move_iterator<_Iterator>& __x,
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/basic_string.h:4989:5: note: candidate template ignored: could not match
      'basic_string<type-parameter-0-0, type-parameter-0-1, type-parameter-0-2>' against 'HasConversionToString'
    operator<(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/basic_string.h:5001:5: note: candidate template ignored: could not match
      'const _CharT *' against 'HasConversionToString'
    operator<(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.0/../../../../include/c++/5.3.0/bits/basic_string.h:5013:5: note: candidate template ignored: could not match
      'const _CharT *' against 'string' (aka 'basic_string<char>')
    operator<(const _CharT* __lhs,
    ^
1 error generated.

而当我将对象显式转换为字符串时,以下代码工作正常。

#include <string>
using namespace std;
struct HasConversionToString {
  HasConversionToString(const string& s_) : s{s_} {}
  string s;
  operator const string&() const { return s; }
};
int main() {
  string s{"a"};
  HasConversionToString obj{"b"};
  return s < static_cast<string>(obj);
}

根据 CPPPreferred 上列出的规则和示例,我认为这没有理由不起作用。我认为 clang 和 gcc 都没有搞砸同一件事,所以我想我有一个概念上的误解。

要调用的是一个函数模板:

template<class charT, class Traits, class Alloc>
bool operator<(std::basic_string<charT, Traits, Alloc> const& lhs,
               std::basic_string<charT, Traits, Alloc> const& rhs);

第二个参数的演绎失败,因为HasConversionToString不是std::basic_string - 模板参数推导不会查看隐式转换。因此,将从重载解析中删除该函数模板。

std::experimental::basic_string_view有一个类似的问题,这个问题通过"足够的附加重载"规则解决了(库必须添加足够的重载,以便basic_string_view和可转换为一个的东西之间的比较有效)。

不过,你真的不希望basic_string有这样的事情 - <可能默默地导致堆的旅行并不是一个好主意。

http://en.cppreference.com/w/cpp/language/template_argument_deduction#Implicit_conversions

类型

推导不考虑隐式转换(类型除外) 上面列出的调整):这是过载解决的工作, 这在以后发生。