1

试验这个问题/答案https://stackoverflow.com/a/50649120/225186我产生了一个似乎是实现循环列表的合法递归自引用类:

struct node{
    int val;
    node const& next;
};

int main(){
    node s{3, {4, s}};

    assert(s.val == 3);
    assert(s.next.val == 4);
    assert(&s.next.next == &s);
    assert(s.next.next.val == 3);
}

但是,当我尝试将其作为更大类的成员时,我会收到来自编译器的警告并且行为会发生变化。

struct A{
    node n;
    int i;
    A(int a, int b) : n{a, {b, n}}{} // warning here, also tried n{a, node{b, n}}
};

int main(){
    A a(3, 4);
    assert( a.n.val == 3 );
    assert(&(a.n.next.next) == &(a.n)); // this assert fail and 
    assert( a.n.next.val == 4 ); // segmentation fault here
}

我得到的警告是gcc: warning: a temporary bound to ‘A::n’ only persists until the constructor exits [-Wextra]. 我不相信警告是正确的,但是它与后来的运行时错误一致。

我承认这个类是非常规的,但是,一个类怎么会改变一个类内部的行为呢?

我错过了什么吗?

4

1 回答 1

2

聚合初始化允许绑定对临时对象的引用(这会导致生命周期延长)。您的第一个示例是聚合初始化,因为node是聚合。

但是,在构造函数成员初始值设定项列表中,将引用绑定到临时 (C++17 class.base.init/11) 是不正确的。这是因为在这种情况下没有生命周期延长,并且允许它不可避免地会产生一个悬空引用。在第二个示例node中不是聚合,因为它具有用户提供的构造函数。

于 2018-06-10T23:46:12.607 回答