Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatic conversion from derived class to base class' member variable's type

Tags:

Long story short: I'd like to understand why the D::operator B() const conversion operator is not used in the last line in the code below, which thus fails when compiling with g++ -std=c++17 source.cpp (compiling with g++ -std=c++2a deleteme.cpp is successful, though).

The error is:

$ g++ -std=c++17 deleteme.cpp && ./a.out 
In file included from /usr/include/c++/10.2.0/cassert:44,
                 from deleteme.cpp:1:
deleteme.cpp: In function ‘int main()’:
deleteme.cpp:19:14: error: no match for ‘operator==’ (operand types are ‘D’ and ‘B’)
   19 |     assert(d == B{2}); // conversion operator not invoked explicitly errors // LINE
      |            ~ ^~ ~~~~
      |            |    |
      |            D    B
In file included from /usr/include/c++/10.2.0/utility:70,
                 from deleteme.cpp:2:
/usr/include/c++/10.2.0/bits/stl_pair.h:466:5: note: candidate: ‘template<class _T1, class _T2> constexpr bool std::operator==(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&)’
  466 |     operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
      |     ^~~~~~~~
/usr/include/c++/10.2.0/bits/stl_pair.h:466:5: note:   template argument deduction/substitution failed:
In file included from /usr/include/c++/10.2.0/cassert:44,
                 from deleteme.cpp:1:
deleteme.cpp:19:20: note:   ‘B’ is not derived from ‘const std::pair<_T1, _T2>’
   19 |     assert(d == B{2}); // conversion operator not invoked explicitly errors // LINE
      |       

The code is:

#include <cassert>
#include <utility>

struct B {
    int x;
    B(int x) : x(x) {}
    bool operator==(B const& other) const { return x == other.x; }
};

struct D : std::pair<B,char*> {
    operator B() const { return this->first; }
};

int main() {
    B b{1};
    D d{std::pair<B,char*>(B{2},(char*)"hello")};

    assert((B)d == B{2}); // conversion operator invoked explicitly is fine
    assert(d == B{2}); // conversion operator not invoked explicitly errors // LINE
}

This question is a follow up to this. There I got help to write a class Recursive which behaves like a pair (so inherits from it) whose first is a std::array and whose second is a boost::hana::optional<std::vector<...>> (see the link for details).

Since the second of the std::pair is kind of an "advanced" information to what's contained in the first, in many places I'd like to cast/convert this object of std::pair-like class Recursive to the type of its first.

like image 460
Enlico Avatar asked Sep 29 '20 20:09

Enlico


1 Answers

When compiler sees d == B{2}, it first creates a list of operator== overloads that it's able to find, and then performs overload resolution on them.

As the link explains, the overload list contains:

  • Member operator==s of the first operand, if any.
  • Non-member operators==s found by unqualified lookup, if any.
  • Built-in operator==s, if your operands can be converted to built-in types.

There's no mention of examining conversion operators of the first operand, so your operator== doesn't get found.

The solution is to make the operator== non-member (possibly define it as a friend). This works:

friend bool operator==(const B &a, const B &b) {return a.x == b.x;}

Starting from C++20, the rules of comparison operators got relaxed: the compiler will look for member operator== in the second operand as well, and will happily call non-member operator== with arguments in a wrong order.

So a == b and b == a are now equivalent to a degree, except that:

  • An operator== called in this new manner must return bool.
  • If given choice, the compiler will prefer the old ways of calling operator== over calling the member operator== of the rhs, which is in turn preferred over calling a non-member operator== with a wrong argument order.
like image 166
HolyBlackCat Avatar answered Oct 03 '22 23:10

HolyBlackCat