未使用的默认成员初始化器如何改变 C++ 中的程序行为?

Posted

技术标签:

【中文标题】未使用的默认成员初始化器如何改变 C++ 中的程序行为?【英文标题】:How unused default member initializer can change program behavior in C++? 【发布时间】:2021-09-22 09:01:05 【问题描述】:

请考虑这个简短的代码示例:

#include <iostream>

struct A

    A()  std::cout << "A() "; 
    ~A()  std::cout << "~A() "; 
;

struct B  const A &a; ;

struct C  const A &a = ; ;

int main()

    B b();
    std::cout << ". ";

    C c();
    std::cout << ". ";

GCC 在此处打印 (https://gcc.godbolt.org/z/czWrq8G5j)

A() ~A() . A() . ~A() 

意味着A-object 初始化引用在b 中的生命周期很短,但在c 中生命周期延长到作用域结束。

结构BC 之间的唯一区别在于默认成员初始化程序,它在main() 中未使用,但行为仍然不同。你能解释一下原因吗?

【问题讨论】:

如果它真的打印出来那么它是编译器中的一个错误。 又一票投给 GCC 错误,代码甚至无法在 MSVC 或 clang 中编译 @AlanBirtles:使用 MSVC Demo 编译。它是 C++20 功能(使用 () 进行聚合初始化),clang 尚不支持,请参阅 compiler_support(聚合的括号初始化)。 @Jarod42,问题应该加C++20标签吗? 是的,很好 【参考方案1】:

C c(...); 是直接初始化的语法。重载解析将从C 的构造函数中找到匹配项:移动构造函数可以通过从 临时实现的C 来调用。 是值初始化,它将使用默认的成员初始化程序。因此,默认成员初始化程序没有被使用。从 C++17 开始,不需要移动构造函数, 直接初始化变量c;在这种情况下,c.a 直接绑定到临时的A,并且生命周期会延长,直到 C 销毁。

B 不是默认可构造的,因此重载决议找不到匹配项。相反,从 C++20 开始使用聚合初始化 - 在此之前它将是格式错误的。 C++20 特性的设计是不改变以前有效程序的行为,因此聚合初始化的优先级低于移动构造函数。

C 的情况不同,临时A 的生命周期不会延长,因为带括号的初始化列表是一个例外情况。如果您使用花括号,它将被扩展:

B b;

【讨论】:

B 的行为不像B() Demo 看起来是正确的解释。在此基础上,我消除了C-object 默认初始化,现在结果一致:gcc.godbolt.org/z/3GxE3qqh5Thanks () 表单不会延长临时对象的生命周期。见eel.is/c++draft/class.temporary#6.10 @T.C.谢谢。那么编译器的行为是正确的。

以上是关于未使用的默认成员初始化器如何改变 C++ 中的程序行为?的主要内容,如果未能解决你的问题,请参考以下文章

C++ Struct 未通过 POD 测试

如何使用 OpenGL 和 GLSL 改变颜色(C++)?

默认情况下,如何将成员初始化程序公开,用于 Swift 中的结构?

在构造函数代码之前禁用默认类成员初始化

C++ 初始化器列表功能:调用函数而不初始化成员?

c2797 未实现成员初始化器列表或非静态数据成员初始化器内的列表初始化