C++中对象间的相互引用
Mutual referencing among objects in C++
我想创建两个对象,它们之间有相互的成员引用。后来它可以扩展到例如N 个引用对象的闭环,其中 N 在编译时已知。
最初的尝试是使用最简单的结构 A
,缺少任何构造函数,这使它成为一个聚合(v
模拟一些有效载荷):
struct A {
const A & a;
int v = 0;
};
struct B {
A a1, a2;
};
consteval bool f()
{
B z{ z.a2, z.a1 };
return &z.a1 == &z.a2.a;
}
static_assert( f() );
不幸的是,由于错误,编译器不接受它:
accessing uninitialized member 'B::a2'
这实际上很奇怪,因为没有进行真正的读取访问,只记住它的地址。演示:https://gcc.godbolt.org/z/cGzYx1Pea
在A
中添加构造函数后问题得到解决,使其不再聚合:
struct A {
constexpr A(const A & a_) : a(a_) {}
constexpr A(const A & a_, int v_) : a(a_), v(v_) {}
const A & a;
int v = 0;
};
现在所有编译器都接受该程序,演示:https://gcc.godbolt.org/z/bs17xfxEs
令人惊讶的是,看似等效的程序修改使其有效。在这种情况下,标准中是否真的有一些措辞阻止使用聚合?究竟是什么让第二个版本安全并被接受?
B z{ z.a2, z.a1 };
尝试复制构造 a1
和 a2
,而不是使用 z.a2
、z.a1
作为第一个字段聚合初始化它们。1
B z{{z.a2, 0}, {z.a1, 0}};
适用于 GCC 和 Clang。 MSVC 给出 error C2078: too many initializers
,这看起来像一个错误。
1 这里,直接列表初始化是为 z
执行的,在这种情况下为每个成员解析为 aggregate initialization, which in turn performs copy-initialization,并且:
[dcl.init.general]/15.6.2
... if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered.
因此,因为初始化器 z.a2
、z.a1
与相应的成员具有相同的类型,所以忽略了成员的聚合性,并使用了复制构造函数。
我想创建两个对象,它们之间有相互的成员引用。后来它可以扩展到例如N 个引用对象的闭环,其中 N 在编译时已知。
最初的尝试是使用最简单的结构 A
,缺少任何构造函数,这使它成为一个聚合(v
模拟一些有效载荷):
struct A {
const A & a;
int v = 0;
};
struct B {
A a1, a2;
};
consteval bool f()
{
B z{ z.a2, z.a1 };
return &z.a1 == &z.a2.a;
}
static_assert( f() );
不幸的是,由于错误,编译器不接受它:
accessing uninitialized member 'B::a2'
这实际上很奇怪,因为没有进行真正的读取访问,只记住它的地址。演示:https://gcc.godbolt.org/z/cGzYx1Pea
在A
中添加构造函数后问题得到解决,使其不再聚合:
struct A {
constexpr A(const A & a_) : a(a_) {}
constexpr A(const A & a_, int v_) : a(a_), v(v_) {}
const A & a;
int v = 0;
};
现在所有编译器都接受该程序,演示:https://gcc.godbolt.org/z/bs17xfxEs
令人惊讶的是,看似等效的程序修改使其有效。在这种情况下,标准中是否真的有一些措辞阻止使用聚合?究竟是什么让第二个版本安全并被接受?
B z{ z.a2, z.a1 };
尝试复制构造 a1
和 a2
,而不是使用 z.a2
、z.a1
作为第一个字段聚合初始化它们。1
B z{{z.a2, 0}, {z.a1, 0}};
适用于 GCC 和 Clang。 MSVC 给出 error C2078: too many initializers
,这看起来像一个错误。
1 这里,直接列表初始化是为 z
执行的,在这种情况下为每个成员解析为 aggregate initialization, which in turn performs copy-initialization,并且:
[dcl.init.general]/15.6.2
... if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered.
因此,因为初始化器 z.a2
、z.a1
与相应的成员具有相同的类型,所以忽略了成员的聚合性,并使用了复制构造函数。