抱歉,标题很笼统,但这是一种令人费解的情况,我无法轻易描述。
假设有以下代码:
struct S
{
S() = default;
int x;
int y;
};
S f()
{
return { 1, 2 };
}
编译并运行得非常好。我想禁止它,因为它很容易出现错误(实际代码要复杂得多)。所以,我尝试添加
template<typename T>
S(std::initializer_list<T>) = delete;
但你猜怎么着 - 没有任何改变。在 Visual Studio 2019 上使用 std=c++17 进行测试。 C++ resharper 将其显示为错误,但 msvc 实际上编译了它并且可以工作。
等等,现在事情变得有趣了。如果将 S() = default;
替换为 S() {}
,则编译失败并显示
'S::S<int>(std::initializer_list<int>)': attempting to reference a deleted function
好吧,这看起来像是与用户定义的构造函数和初始化有关?!凌乱,但还可以理解。
但是等等 - 它变得更有趣 - 保留 = default
构造函数,but 使字段 private
也会改变这种行为和猜测什么 - 该错误与无法访问的成员无关,但它再次显示了上面的错误!
因此,为了使此删除工作有效,我应该将字段设为私有(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
构造函数、公共(public)字段,但删除了初始值设定项列表构造函数)。
知道会发生什么吗?
最佳答案
在 C++17 中,S
被视为聚合,因此您没有调用任何构造函数,基本上是直接初始化成员。如果您更改为使用 C++20,S
不再被视为聚合,因为规则已更改并且代码将按预期工作。
更改访问说明符有效的原因是聚合的所有非静态数据成员的访问说明符都需要是公共(public)的。让它们成为非公共(public)意味着您的类不再是聚合,并且您不再获得聚合初始化,而是尝试进行列表初始化,但删除的构造函数会失败。
关于c++ - 尝试删除初始化列表构造函数并不总是有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69932096/