为什么初始化列表中的自定义参考不是错误

Why self-initialize reference in initializer list is not an error?

本文关键字:参考 错误 自定义 初始化 列表 为什么      更新时间:2023-10-16

我遇到了一个奇怪的问题。如果我尝试编译自我缓冲对象参考,我的编译器(对于ESP32(不会显示任何错误或警告。我调查了这个问题,发现某些编译器不会显示此代码的任何错误或警告:

#include <iostream>
#include <string>
class Foo
{
    public:
        std::string s;
        Foo(){ std::cout << "Foo()n"; }
        std::string ToString() { return s; }
};
class Bar
{
    public:
        Foo& foo;
        Bar(): foo(foo) { std::cout << "Bar()n"; }
        std::string ToString() { return foo.ToString(); }
};
int main()
{
    Bar bar;
    std::cout << "STARTn" << bar.foo.ToString() << "nENDn";
}

只有叮当声显示了关于自我分配的警告,但即使如此显然应该是错误的。是否有任何情况可以合法?

是否有任何情况可能是合法的?

否。[dcl.ref]/5状态:

应初始化引用以参考有效对象或函数。

您的示例涉及以不参考有效对象的方式初始化参考文献,因此它是不形成的。总的来说,这并不总是出于明显的原因诊断...但是在这里,很明显,这是一个错误,这就是为什么GCC和Clang警告它的原因。

只有叮当声显示了有关自我分配的警告,但即使如此,这也显然应该是错误的。

该标准并没有真正处理什么是警告和错误。它只是处理诊断。为什么这些编译器选择将其诊断为警告而不是错误?耸耸肩。

无论如何,这是一个容易纠正的问题。如果您想要错误,请使用-Werror编译。现在是GCC和Clang上的错误。无论如何,这通常是一个好习惯。

请注意,编译器拒绝为标准所说的不形式不正确(而不是未定义(产生可执行文件,这是技术上不合格的,这就是编译器可以't默认为-Werror。编译器还必须证明您程序的每一个可能的执行路径都会调用这种不确定的行为,这通常是不可能的。

标准不说您的代码不明显,只需未定义,如果您调用此构造函数

现在您可能会说,为什么标准不将其声明为不明显,因为这显然是不正确的?原因是复杂性。标准已经很大。在很多情况下,标准可以 识别无条件不确定行为的子集,这些子集在编译时可以证明并使它们变得不明显,但是这样做将是一项非常大的事业,并添加了更多的特别特殊案件已经很大。

因此,在这些情况下,将其留给编译器发出警告,并让用户选择加入这些警告,将其转换为错误。

因为自定义的参考是不确定的行为,因此编译器可以免费诊断消息。

[basic.life]/2说

参考的初始化时,参考的寿命开始 完成。

因此,在初始化期间,foo的寿命不会开始。然后,根据[Expr.Type]/1(强调我的(:

如果表达式最初具有"对t"的类型([dcl.ref],[dcl.init.ref](,则在进行进一步分析之前将类型调整为t。该表达式指定用参考表示的对象或函数,表达式是lvalue或xvalue,具体取决于表达式。[注意:参考的寿命开始或结束后,行为是不确定的(请参阅[basic.life](。 - end Note]

调整初始化器表达式中foo的类型会导致不确定的行为。