using user-defined conversions with implicit conversions in comparisons

匿名 (未验证) 提交于 2019-12-03 03:04:01

问题:

I am struggling to understand why the following code does not allow an implicit conversion to occur.

#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; } 

Both clang and gcc fail to find a valid way to compare the two objects with errors along the lines of:

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. 

whereas the following code works fine, when I explicitly cast the object to a string.

#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); } 

based on the rules and examples listed on cppreference for implicit casts, I see no reason this shouldn't work. I assume that both clang and gcc didn't screw up the same thing, so I imagine that I've got a conceptual misunderstanding.

回答1:

The one you want to call is a function template:

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); 

Deduction fails for the second argument because a HasConversionToString is not a std::basic_string - template argument deduction doesn't look through implicit conversions. As a result, that function template is removed from overload resolution.

std::experimental::basic_string_view has a similar problem, which was solved by a "sufficient additional overloads" rule (the library must add enough overloads so that comparison between a basic_string_view and something convertible to one works).

You don't really want such a thing for basic_string, though - having < possibly silently causing a trip to the heap is not really a good idea.



回答2:

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

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.



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