Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using user-defined conversions with implicit conversions in comparisons

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.

like image 554
David Avatar asked Feb 22 '16 01:02

David


People also ask

What is the difference between implicit conversion and explicit type conversion?

An implicit conversion does not require any special syntax in the source code. In the following example, Visual Basic implicitly converts the value of k to a single-precision floating-point value before assigning it to q . An explicit conversion uses a type conversion keyword.

Which conversion can be performed implicitly?

Implicit type conversion in C language is the conversion of one data type into another datatype by the compiler during the execution of the program. It is also called automatic type conversion.

What is user-defined conversion?

For more information, see Standard Conversions. User-defined conversions perform conversions between user-defined types, or between user-defined types and built-in types. You can implement them as Conversion constructors or as Conversion functions.

What is the example of implicit conversion?

Implicit conversions: No special syntax is required because the conversion always succeeds and no data will be lost. Examples include conversions from smaller to larger integral types, and conversions from derived classes to base classes.


1 Answers

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.

like image 194
T.C. Avatar answered Sep 29 '22 10:09

T.C.