首页 > 解决方案 > 如果构造函数被显式默认或删除,为什么自 C++20 以来聚合初始化不再起作用?

问题描述

我正在将 C++ Visual Studio 项目从 VS2017 迁移到 VS2019。

我现在收到一个错误,以前没有发生过,可以用这几行代码重现:

struct Foo
{
    Foo() = default;
    int bar;
};
auto test = Foo { 0 };

错误是

(6): 错误 C2440: 'initializing': 无法从 'initializer list' 转换为 'Foo'

(6):注意:没有构造函数可以采用源类型,或者构造函数重载决议不明确

/std:c++latest该项目是用标志编译的。我在godbolt上复制了它。如果我将它切换到/std:c++17,它可以像以前一样编译。

我尝试使用clang with编译相同的代码-std=c++2a并得到类似的错误。此外,默认或删除其他构造函数会生成此错误。

显然,在 VS2019 中添加了一些新的 C++20 功能,我假设此问题的根源在https://en.cppreference.com/w/cpp/language/aggregate_initialization中进行了描述。那里说聚合可以是一个结构(在其他标准中)具有

请注意,括号中的部分“允许显式默认或删除的构造函数”已被删除,并且“用户提供”更改为“用户声明”。

所以我的第一个问题是,我是否正确假设标准的这种变化是我的代码以前编译但现在不再编译的原因?

当然,解决这个问题很容易:只需删除显式默认的构造函数。

但是,我在所有项目中都显式地默认并删除了很多构造函数,因为我发现以这种方式使代码更具表现力是一个好习惯,因为与隐式默认或删除的构造函数相比,它只会产生更少的意外。然而,随着这种变化,这似乎不再是一个好习惯了......

所以我的实际问题是: 从 C++17 到 C++20 的这种变化背后的原因是什么?这种向后兼容性的突破是故意的吗?是否有一些权衡,比如“好吧,我们在这里打破了向后兼容性,但这是为了更大的利益。”?这更大的好处是什么?

标签: c++c++17backwards-compatibilityc++20

解决方案


P1008的摘要,导致更改的提案:

C++ 目前允许通过聚合初始化来初始化一些具有用户声明的构造函数的类型,绕过那些构造函数。结果是令人惊讶、混乱和错误的代码。本文提出了一种修复方法,使 C++ 中的初始化语义更安全、更统一且更易于教授。我们还讨论了此修复引入的重大更改。

他们给出的例子之一如下。

struct X {
  int i{4};
  X() = default;
};

int main() {
  X x1(3); // ill-formed - no matching c’tor
  X x2{3}; // compiles!
}

对我来说,很明显,提议的更改值得他们承担的向后不兼容。= default事实上,聚合默认构造函数似乎不再是一个好习惯。


推荐阅读