未使用的默认成员初始值设定项如何改变 C++ 中的程序行为?

问题描述

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

#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() 

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

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

解决方法

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

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

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

B b{{}};