c++ - 尝试删除初始化列表构造函数并不总是有效

标签 c++ c++11 c++17 initializer-list

抱歉,标题很笼统,但这是一种令人费解的情况,我无法轻易描述。

假设有以下代码:

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/

相关文章:

c++ - 通过 lambda 定义的 constexpr 递归函数

c++ - const 成员函数只能调用 const 成员函数吗?

c++ - 如何在 OpenGL/GLSL 中对特定颜色进行阈值处理

c++ - g++ 相对路径

windows - C++11:如何实现快速、轻量级、公平的同步资源访问

c++ - 从 VS2013 到 VS2017 std::async 不启动新线程

c++ - 当该参数的类型是模板类型时,如何提供默认参数参数?

c++ - decltype 的模板参数数量不正确。

c++ - 为什么在 ctor 的参数列表中用 `x` 替换成员 `decltype(x)` 的类型会破坏类模板参数推导?

c++ - noexcept 访问 std::variant