Home > Enterprise >  User defined operator conversion resolution order
User defined operator conversion resolution order

Time:10-25

It is possible to configure the resolution order of user defined operators? Consider the following code:

#include <memory>
#include <utility>

using P = std::unique_ptr<int>;

struct S{
  P p;

  operator P() && { return std::move(p); }
  operator const P&() const { return p; }
};

S s{std::make_unique<int>()};
P p;
p = std::move(s);

This fails to compile

In file included from /opt/compiler-explorer/gcc-12.1.0/include/c  /12.1.0/memory:76,
                 from <source>:1:
/opt/compiler-explorer/gcc-12.1.0/include/c  /12.1.0/bits/unique_ptr.h:406:19: note: candidate: 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = int; _Dp = std::default_delete<int>]'
  406 |       unique_ptr& operator=(unique_ptr&&) = default;
      |                   ^~~~~~~~
/opt/compiler-explorer/gcc-12.1.0/include/c  /12.1.0/bits/unique_ptr.h:515:19: note: candidate: 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]' (deleted)
  515 |       unique_ptr& operator=(const unique_ptr&) = delete;

It seems compilation is failing because of an ambiguity between the deleted copy assignment and defaulted move assignment operators. Why is the compiler unable to resolve the operation here and is there a way to define the operators to make this work?

CodePudding user response:

You didn't define conversion/cast operators, you defined call operators. As written, you'd need to do:

p = std::move(s)();

to call s.

If you want a user-defined conversion, you'd want to change:

P operator() && { return std::move(p); }
const P& operator() const { return p; }

to something like:

operator P() && { return std::move(p); }
operator P() const { return p; }

though I'm not sure whether that paired overload of a conversion operator is legal.

CodePudding user response:

If these operators are supposed to be conversion operators, you can define them like this:

operator P() && { return std::move(p); }
operator const P&() & { return p; }

Demo

The second operator needs the lvalue ref-qualifier (the & at the end) because without it, the implicit object parameter can also be an rvalue. That case is also covered with the rvalue ref-qualified version, so the call becomes ambiguous. Same thing if you add the const qualifier, because an rvalue can bind to a const lvalue reference.

  • Related