自定义类型 std::variant 的赋值运算符删除了特殊成员函数?
Assignment operator of std::variant of custom type with deleted special member functions?
考虑:
#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 接受了它,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{};
^
我应该信任哪个编译器?
编辑
最初,没有language-lawyer
标签,这就是为什么我使用cppreference进行分析的原因。但是,查看最新草案 (relevant section),我没有发现任何会使它无效的内容。
我认为 MSVC 不正确。根据 documentation:
- 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
;
对于一些具有转发引用参数并使用 A{}
参数调用它的虚构函数,A x[] = { std::forward<T>(t) };
无效而 B x[] = { std::forward<T>(t) };
有效。因此,T_j
应该解析为 B
。现场演示:https://godbolt.org/z/fM67e7oGj.
然后:
- If
*this
already holds a T_j
...
这不适用,因为 v
不包含 B
。
下一个:
- Otherwise, if
std::is_nothrow_constructible_v<T_j, T> || !std::is_nothrow_move_constructible_v<T_j>
is true
...
这也不适用,因为这个表达式是false
;现场演示:https://godbolt.org/z/x674rnbcj (and, MSVC agrees on that: https://godbolt.org/z/5Techn8jG).
最后:
- Otherwise, equivalent to
this->operator=(variant(std::forward<T>(t)))
.
但是,此调用无法使用 MSVC 进行编译:https://godbolt.org/z/cWr4f6EhK。
考虑:
#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 接受了它,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{};
^
我应该信任哪个编译器?
编辑
最初,没有language-lawyer
标签,这就是为什么我使用cppreference进行分析的原因。但是,查看最新草案 (relevant section),我没有发现任何会使它无效的内容。
我认为 MSVC 不正确。根据 documentation:
- Converting assignment.
Determines the alternative type
T_j
that would be selected by overload resolution for the expressionF(std::forward<T>(t))
if there was an overload of imaginary functionF(T_i)
for everyT_i
fromTypes...
in scope at the same time, except that:
- An overload
F(T_i)
is only considered if the declarationT_i x[] = { std::forward<T>(t) };
is valid for some invented variablex
;
对于一些具有转发引用参数并使用 A{}
参数调用它的虚构函数,A x[] = { std::forward<T>(t) };
无效而 B x[] = { std::forward<T>(t) };
有效。因此,T_j
应该解析为 B
。现场演示:https://godbolt.org/z/fM67e7oGj.
然后:
- If
*this
already holds aT_j
...
这不适用,因为 v
不包含 B
。
下一个:
- Otherwise, if
std::is_nothrow_constructible_v<T_j, T> || !std::is_nothrow_move_constructible_v<T_j>
istrue
...
这也不适用,因为这个表达式是false
;现场演示:https://godbolt.org/z/x674rnbcj (and, MSVC agrees on that: https://godbolt.org/z/5Techn8jG).
最后:
- Otherwise, equivalent to
this->operator=(variant(std::forward<T>(t)))
.
但是,此调用无法使用 MSVC 进行编译:https://godbolt.org/z/cWr4f6EhK。