未使用的默认成员初始值设定项如何改变 C++ 中的程序行为?
How unused default member initializer can change program behavior in 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()
意味着 A
-对象初始化引用的生命周期在 b
中很短,但在 c
中生命周期延长到范围结束。
结构 B
和 C
之间的唯一区别在于默认成员初始化器,它在 main() 中 未使用,但行为仍然不同.你能解释一下为什么吗?
C c(...);
是直接初始化的语法。重载解析会从 C
的构造函数中找到匹配项:移动构造函数可以通过 {}
中的 C
的临时物化来调用。 {}
是将使用默认成员初始化程序的值初始化。因此,默认成员初始化程序并非未使用。从 C++17 开始,移动构造函数不是必需的,{}
直接初始化变量 c
;在这种情况下 c.a
直接绑定到临时 A
并且生命周期延长直到 C
.
被破坏
B
不是默认可构造的,因此重载解析不会找到匹配项。相反,从 C++20 开始使用聚合初始化 - 在此之前它的格式不正确。 C++20 特性的设计是不改变以前有效程序的行为,因此聚合初始化的优先级低于移动构造函数。
与 C
的情况不同,临时 A
的生命周期没有延长,因为带括号的初始化列表是一个例外情况。如果您使用大括号,它将被扩展:
B b{{}};
请考虑这个简短的代码示例:
#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
-对象初始化引用的生命周期在 b
中很短,但在 c
中生命周期延长到范围结束。
结构 B
和 C
之间的唯一区别在于默认成员初始化器,它在 main() 中 未使用,但行为仍然不同.你能解释一下为什么吗?
C c(...);
是直接初始化的语法。重载解析会从 C
的构造函数中找到匹配项:移动构造函数可以通过 {}
中的 C
的临时物化来调用。 {}
是将使用默认成员初始化程序的值初始化。因此,默认成员初始化程序并非未使用。从 C++17 开始,移动构造函数不是必需的,{}
直接初始化变量 c
;在这种情况下 c.a
直接绑定到临时 A
并且生命周期延长直到 C
.
B
不是默认可构造的,因此重载解析不会找到匹配项。相反,从 C++20 开始使用聚合初始化 - 在此之前它的格式不正确。 C++20 特性的设计是不改变以前有效程序的行为,因此聚合初始化的优先级低于移动构造函数。
与 C
的情况不同,临时 A
的生命周期没有延长,因为带括号的初始化列表是一个例外情况。如果您使用大括号,它将被扩展:
B b{{}};