5

考虑:

#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 接受了它,而 GCC 和 Clang 以相同的错误消息拒绝了它

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{};
    ^

我应该信任哪个编译器?

4

1 回答 1

3

编辑

最初,没有language-lawyer标签,这就是我使用cppreference进行分析的原因。但是,查看最新草案(相关部分),我没有看到任何会使它无效的东西。


我相信 MSVC 是不正确的。根据文档

  1. 转换赋值。
  • 如果范围内的每个from同时存在虚函数的重载,则确定T_j将由重载决议为表达式选择的替代类型,但以下情况除外:F(std::forward<T>(t))F(T_i)T_iTypes...

    • 仅当声明对某些发明变量有效F(T_i) 时才考虑T_i x[] = { std::forward<T>(t) };重载x

对于一些具有转发引用参数并使用A{}参数调用它的虚构函数,在 isA x[] = { std::forward<T>(t) };时是无效B x[] = { std::forward<T>(t) };的。因此,T_j应解决为B. 现场演示:https ://godbolt.org/z/fM67e7oGj 。

然后:

  • 如果*this已经持有T_j...

这不适用,因为v不持有B.

下一个:

  • 否则,如果std::is_nothrow_constructible_v<T_j, T> || !std::is_nothrow_move_constructible_v<T_j>true...

这也不适用,因为这个表达式是false; 现场演示:https ://godbolt.org/z/x674rnbcj (并且,MSVC 同意这一点:https ://godbolt.org/z/5Techn8jG )。

最后:

  • 否则,等价于this->operator=(variant(std::forward<T>(t)))

但是,此调用无法使用 MSVC 编译:https ://godbolt.org/z/cWr4f6EhK 。

于 2021-08-31T12:50:27.600 回答