来自空大括号的模糊复制赋值的编译器差异
我一直在试图理解std::nullopt_t不允许DefaultConstructible在 C++17(引入它的地方)及更高版本中的基本原理,并在此过程中解决了一些编译器差异混淆。
考虑以下违反规范的(它是DefaultConstructible)实现nullopt_t:
struct nullopt_t {
explicit constexpr nullopt_t() = default;
};
它是 C++11 和 C++14(无用户提供的构造函数)中的聚合,但不是 C++17(构造函数explicit)和 C++20(用户声明的构造函数)中的聚合。
现在考虑以下示例:
struct S {
constexpr S() {}
S(S const&) {}
S& operator=(S const&) { return *this; } // #1
S& operator=(nullopt_t) { return *this; } // #2
};
int main() {
S s{};
s = {}; // GCC error: ambiguous overload for 'operator=' (#1 and #2)
}
这在 C++11 到 C++20 中被 GCC(各种版本,比如 v11.0)拒绝,但在 C++11 到 C++11 中被 Clang(比如 v12.0)和 MSVC(v19.28)接受C++20。
演示
我最初的假设是该程序:
- 在 C++11 和 C++14 中格式错误,因为
nullopt_t(如上)是一个聚合,而它 - 是公形成式C ++ 17和C ++ 20,因为它不再是一种聚集体,这意味着它的显式默认的构造应该禁止的临时副本列表-INIT
nullopt_t根据需要用于在拷贝赋值运算符对象#2是可行的,
但是没有一个编译器完全同意这个理论,有些我可能遗漏了一些东西。
什么编译器在这里是正确的(如果有的话),我们如何通过相关的标准部分(和 DR:s,如果相关)来解释它?