3

Is it 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? Is there a way to define the operators to make this work?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Deev
  • 471
  • 2
  • 9

2 Answers2

0

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.

Nelfeal
  • 12,593
  • 1
  • 20
  • 39
  • Good point. Updated the question. But this still doesn't compile because of the linked error message. – Deev Oct 24 '22 at 23:46
  • 1
    @Deev It does compile... added a link to compiler explorer. – Nelfeal Oct 24 '22 at 23:49
  • Interesting. It seems to not compile without the `&` https://godbolt.org/z/jacWadqoc – Deev Oct 24 '22 at 23:50
  • @Deev Added some explanations. – Nelfeal Oct 24 '22 at 23:54
  • Just so you know, I asked [another question](https://stackoverflow.com/questions/74188070/conversion-operator-with-ref-qualifers-rvalue-ref-and-const-lvalue-ref-overload) regarding the `const&` qualifier. The explanation I have doesn't satisfy me because it works with normal member functions. – Nelfeal Oct 25 '22 at 00:40
-1

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.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Since when do you specify the return type of a conversion operator? – Nelfeal Oct 24 '22 at 23:37
  • @Nelfeal: Yeah, was already fixing that (your comment came in half a minute before I finished edit). Copy-and-paste error. Frankly, not sure whether the end result is legal anyway (it's a weird use case, and not one I've ever had much cause to explore). – ShadowRanger Oct 24 '22 at 23:38
  • Fixed the example. But the code still doesn't compile. – Deev Oct 24 '22 at 23:49