gpt4 book ai didi

c++ - 在成员初始化程序中创建临时对象时销毁它的点

转载 作者:行者123 更新时间:2023-12-03 10:03:34 24 4
gpt4 key购买 nike

#include <iostream>
struct A{
A(int){

}
~A(){
std::cout<<"A destroy\n";
}
};
struct B{
B(int){
std::cout<<"B construct\n";
}
~B(){
std::cout<<"B destroy\n";
}
};
struct Content{
A const& a;
};
struct Data{
Data():c{0},b{0}{

}
Content c;
B b;
};
int main(){
Data d;
std::cout<<"exit\n";
}
GCC的输出是:
B construct
A destroy
exit
B destroy
Clang 提示这段代码格式不正确。 Here是两个编译器的性能。
关于 Clang 报错的问题,标准中确实有一条相关的规则,那就是:
[class.init#class.base.init-8]

A temporary expression bound to a reference member in a mem-initializer is ill-formed.


我不确定 Clang 是否理解过度了?在我看来,规则似乎是说 命名的引用成员内存初始化程序 ID mem-initializer 不应该绑定(bind)到临时表达式。在我的示例中,成员 c类(class) Data不是引用。
据推测, Clang将其视为使引用成员绑定(bind)到临时表达式的引用成员的任何初始化都发生在成员初始化程序中,都是格式错误的。所以我举了一个例子来检查是否 Clang也这样觉得。
struct A{
int const& rf;
};
struct B{
B():a(new A{0}){}
A* a;
};
int main(){
B b;
delete b.a;
}
gives警告但不是错误。所以,我不确定 Clang也这样觉得。我不知道它是如何理解规则的?
如果第一个例子本身是有效的,我会考虑 GCC不遵守标准。因为破坏临时对象的顺序。
[class.temporary#4]

Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created.


临时对象将在成员初始化器的完整表达式末尾被销毁,在我的示例中,即 c{0} .但是, GCC在子对象 b 之后销毁临时对象已建成。我认为这是第一个问题。
实际上,临时绑定(bind)的引用并不是这里列出的异常(exception):
[class.temporary#6]

The temporary object to which the reference is bound or the temporary object that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference
The exceptions to this lifetime rule are:

  • A temporary object bound to a reference parameter in a function call ([expr.call]) persists until the completion of the full-expression containing the call.
  • A temporary object bound to a reference element of an aggregate of class type initialized from a parenthesized expression-list ([dcl.init]) persists until the completion of the full-expression containing the expression-list.
  • The lifetime of a temporary bound to the returned value in a function return statement ([stmt.return]) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
  • A temporary bound to a reference in a new-initializer ([expr.new]) persists until the completion of the full-expression containing the new-initializer.


也不是,也就是说,我的第一个例子不是上面列表中列出的异常(exception)。因此,临时对象的生命周期应该与子对象 a 的生命周期相同。子对象的 c对象 b ,它们都具有与 b 相同的生命周期.那么,为什么 GCC这么早就销毁临时对象?临时对象不应该和对象一起销毁吗 bmain ?我想这里是 GCC的第二期.不知道怎么 Clang处理临时对象,因为它之前给出了错误。
问题:
  • Clang报告第一个示例的错误对吗?如果是正确的,[class.init#class.base.init-8] 是否应该更清楚?
  • 如果 Clang过度理解[class.init#class.base.init-8],是性能GCC破坏临时对象被认为是一个错误?或者,exceptions忽略这个案例?即使异常(exception)忽略了这种情况(引用绑定(bind)发生在成员初始化器中),我仍然认为 GCC有错,临时不应该在构造c{0}之前排序的完整表达式(b)的末尾被破坏吗? .

  • 如何解读以上这些问题?

    最佳答案

    Clang 是正确的,但是是的,标准可能更清晰。
    [class.temporary]/6的效果| (通过引用绑定(bind)延长临时表达式生命周期)是为了确保除了列出的异常之外,绑定(bind)到引用的临时对象的生命周期延长到引用的生命周期。但是,当引用是类非静态数据成员时,引用的生命周期在绑定(bind)点(这发生在(可能是默认的)构造函数中)不是静态已知的,因此临时可以延长生命周期。由于非静态数据成员不包括在异常列表中,因此必须通过其他方式防止这种情况发生,并且确实可以通过 [class.base.init] 中的 IF 案例来防止。 :

    8 - A temporary expression bound to a reference member in a mem-initializer is ill-formed.


    11 - A temporary expression bound to a reference member from a default member initializer is ill-formed.


    我们必须得出结论,这种语言的意图是呈现格式错误的任何尝试,即从类的(可能是默认的)构造函数中将临时绑定(bind)到类数据成员,因为否则临时将有资格延长生命周期,这将是荒谬的(其中引用的生命周期是静态未知的)。因此,这必须包括子聚合的引用成员;标准中包含一个说明这一点的说明是合适的。
    值得考虑将临时表达式绑定(bind)到引用属于 3 种情况:
  • [class.base.init]/8 和/11 下的 IF(类非静态数据成员)
  • [class.temporary]/6 下的异常(exception)情况;临时在完整表达式结束时被销毁。
  • 否则,会发生生命周期延长。

  • 因此,如果编译器不拒绝,并且代码不属于 [class.temporary]/6 中的异常之一,并且编译器不执行生命周期扩展(到引用的完整生命周期),则编译器是有过错。
    MSVC 也错误地 accepts您的代码和输出:
    main
    A(int)
    ~A()
    B(int)
    D()
    exit
    ~B()
    一个有趣的情况是,包含引用非静态数据成员的类是一个聚合,因此有资格通过列表初始化语法进行聚合初始化(示例改编自 CWG 1815):
    struct A {};
    struct C { A&& a = A{}; };
    C c1; // #1
    C c2{A{}}; // #2
    C c3{}; // #3
    C c4 = C(); // #4
    这里 #1#4格式不正确,尽管 gcc 和 MSVC 错误地接受。 #3根据 [class.base.init]/11 标准的当前措辞格式错误,但这与 CWG 1815 中所述的委员会的意图相反:

    Notes from the February, 2014 meeting:

    CWG agreed with the suggested direction, which would treat #3 in the example like #2 and make the default constructor deleted


    也就是说, #3将是有效的,并导致生命周期延长; gcc and clang do so, but MSVC fails to perform lifetime extension in either #2 or #3 ; icc performs lifetime extension for #2 only .奇怪的是,clang 报告了一个诊断,声称尽管这样做了,但它不会执行生命周期延长!:

    warning: sorry, lifetime extension of temporary created by aggregate initialization using default member initializer is not supported; lifetime of temporary will end at the end of the full-expression [-Wdangling]


    我问过 Validity and/or lifetime extension of mem-initializer in aggregate initialization .
    请注意(假设这是允许的)它仅适用于 c3是一个完整的对象;如果它是成员对象(如您的第一个示例中的 c)[class.base.init] 将明确适用并且 c3也将是不正确的。

    关于c++ - 在成员初始化程序中创建临时对象时销毁它的点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65445422/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com