Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assignment operator of std::variant of custom type with deleted special member functions?

Consider:

#include <variant>

struct A { 
  A() = default;
  A(A&&) = delete;
};

struct B { 
  B() = delete;
  B(A&&) {};
};

int main() {
  std::variant<A, B> v{};
  v = A{};
}

MSVC accepted it, while GCC and Clang rejected it with the same error message:

opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/12.0.0/../../../../include/c++/12.0.0/variant:1465:3: error: call to deleted member function 'operator='
                operator=(variant(std::forward<_Tp>(__rhs)));
                ^~~~~~~~~
<source>:15:5: note: in instantiation of function template specialization 'std::variant<A, B>::operator=<A>' requested here
  v = A{};
    ^

Which compiler should I trust?

like image 767
康桓瑋 Avatar asked Oct 14 '22 20:10

康桓瑋


1 Answers

EDIT

Initially, there was no language-lawyer tag, which was why I used cppreference for the analysis. However, looking into the latest draft (relevant section), I don't see anything that would make it invalid.


I believe MSVC is incorrect. According to the documentation:

  1. Converting assignment.
  • Determines the alternative type T_j that would be selected by overload resolution for the expression F(std::forward<T>(t)) if there was an overload of imaginary function F(T_i) for every T_i from Types... in scope at the same time, except that:

    • An overload F(T_i) is only considered if the declaration T_i x[] = { std::forward<T>(t) }; is valid for some invented variable x;

With some imaginary function having forwarding reference parameter and calling it with A{} argument, A x[] = { std::forward<T>(t) }; is not valid while B x[] = { std::forward<T>(t) }; is. Consequently , T_j should be resolved as B. Live demo: https://godbolt.org/z/fM67e7oGj.

Then:

  • If *this already holds a T_j...

This does not apply, since v does not hold a B.

Next:

  • Otherwise, if std::is_nothrow_constructible_v<T_j, T> || !std::is_nothrow_move_constructible_v<T_j> is true...

This also does not apply, since this expression is false; live demo: https://godbolt.org/z/x674rnbcj (and, MSVC agrees on that: https://godbolt.org/z/5Techn8jG).

Finally:

  • Otherwise, equivalent to this->operator=(variant(std::forward<T>(t))).

However, this call does not compile with MSVC: https://godbolt.org/z/cWr4f6EhK.

like image 71
Daniel Langr Avatar answered Oct 24 '22 00:10

Daniel Langr