【发布时间】:2021-12-24 03:54:57
【问题描述】:
对于通用标题,我很抱歉,但这是一个令人发指的情况,我无法轻易描述。
假设如下代码:
struct S
{
S() = default;
int x;
int y;
};
S f()
{
return { 1, 2 };
}
这编译并且工作得非常好。我想禁止它,因为它容易出错(实际代码要复杂得多)。所以,我尝试添加
template<typename T>
S(std::initializer_list<T>) = delete;
但你猜怎么着 - 没有任何变化。使用 std=c++17 在 Visual Studio 2019 上测试。 C++ resharper 将此显示为错误,但 msvc 实际上会编译它并且它可以工作。
等等,现在变得有趣了。如果将S() = default; 替换为S() {},则编译失败并显示
'S::S<int>(std::initializer_list<int>)': attempting to reference a deleted function
好的,这看起来与用户定义的构造函数和初始化有关?!乱七八糟,但还算可以理解。
但是等等——它变得更有趣了——保留= default构造函数,但是使字段private也改变了这种行为,你猜怎么着——错误与不可访问的成员无关,但它再次显示上面的错误!
所以,为了使这个删除工作,我应该将字段设为私有或定义我自己的空构造函数(忽略未初始化的 x 和 y 字段,这只是一个简化的示例),意思是:
struct S
{
S() = default;
// S() {}
template<typename T>
S(std::initializer_list<T>) = delete;
private:
int x;
int y;
};
clang 13 和 GCC 11 的行为方式完全相同,而 GCC 9.3 无法编译原始代码(使用 =default 构造函数,公共字段,但删除了初始化列表构造函数)。
任何想法会发生什么?
【问题讨论】:
-
如果将 MSVC 更改为使用 C++20 编译会发生什么?在 C++17 中,
S被视为聚合,您使用聚合初始化而不是调用任何类型的构造函数 -
@NathanOliver - 你是对的,C++20 抱怨这个。
-
那么,什么是禁止这种行为的正确方法呢?为什么
public/private会修改这种行为? -
聚合的所有成员的访问说明符必须相同。让他们不同意味着你的班级不再是一个聚合体。如果您可以与成员私下相处,那么我会这样做。如果可以使用 C++20,那就更好了。
-
@NathanOliver 谢谢,你能发布答案吗?我会接受它,完全有道理。不过这很疯狂。
标签: c++ c++11 c++17 initializer-list