C++ 根据C++;标准

C++ 根据C++;标准,c++,undefined-behavior,C++,Undefined Behavior,(我知道,应该避免返回函数本地变量的地址/引用,程序永远不应该这样做。) 返回对局部变量/引用的引用是否会导致未定义的行为?或者,当使用返回的引用(或“取消引用”)时,未定义的行为只会在以后发生吗 i、 e.下面的代码示例调用未定义行为的确切语句(#1或#2或#3)是什么?(我把我的理论写在每个理论的旁边) #包括 结构A { 国际货币基金组织; A():m_i(10) { } }; A&foo() { A A; a、 m_i=20; 返回a; } int main() {

(我知道,应该避免返回函数本地变量的地址/引用,程序永远不应该这样做。)


返回对局部变量/引用的引用是否会导致未定义的行为?或者,当使用返回的引用(或“取消引用”)时,未定义的行为只会在以后发生吗

i、 e.下面的代码示例调用未定义行为的确切语句(
#1
#2
#3
)是什么?(我把我的理论写在每个理论的旁边)

#包括
结构A
{ 
国际货币基金组织;
A():m_i(10)
{
} 
};  
A&foo()
{     
A A;
a、 m_i=20;
返回a;
} 
int main()
{
foo();/#1-非UB;从未使用返回值
常量&ref=foo();/#2-非UB;返回值仍未使用

std::cout以下是我对此事的不完整和可能不充分的看法:

引用的唯一特殊之处在于,在初始化时它们必须引用一个有效的对象。如果该对象以后不再存在,则使用该引用是UB,并且初始化另一个引用到现在已失效的引用也是UB

下面这个简单得多的例子提供了与您的问题完全相同的困境,我认为:

std::reference_wrapper<T> r;

{
    T t;
    r = std::ref(t);
}

// #1
std::reference\r;
{
T;
r=std::ref(t);
}
// #1
在#1处,
r
中的引用不再有效,但程序正常。只是不要读取
r

在您的示例中,第#1行是可以的,而第#2行不是——这是因为原始的第#2行使用参数
foo()
调用
A::A(常数&)
,正如前面所讨论的,这无法使用有效的引用初始化函数参数变量,编辑的版本
A const&A=foo()也是如此

我想说#3.即使引用的对象已经超出范围,但#2实际上并没有做任何事情。这实际上不是一个与标准相关的问题,因为它是连续出现两个错误的结果:

  • 返回对范围外对象的引用,后跟
  • 引用的使用

  • 两者中的任何一个都定义了行为。标准是否对超过其生命周期的对象引用的使用有任何规定是另一回事。

    当然,当引用首次初始化时,它是有效的,满足以下条件:

    [C++11:8.3.2/5]:
    不应有对引用的引用、引用数组和引用指针。引用声明应包含初始值设定项(8.5.3),除非声明包含显式外部说明符(7.1.1)、类成员(9.2)类定义中的声明,或者是参数或返回类型的声明(8.3.5);参见3.1。引用应初始化为引用有效的对象或函数。[注:特别是,在定义良好的程序中不能存在空引用,因为创建此类引用的唯一方法是将其绑定到通过取消引用空指针获得的“对象”,这会导致未定义的行为。如9.6所述,引用不能直接绑定到位字段。-结束注]

    从函数返回的引用是一个xvalue:

    [C++11:3.10/1]:
    […]xvalue(一个“到期”值)也指一个对象,通常在其生命周期结束时(例如,它的资源可能会被移动)。xvalue是涉及右值引用的某些类型表达式的结果(8.3.2)。[示例:调用返回类型为右值引用的函数的结果是xvalue.-结束示例][…]

    这意味着以下内容不适用:

    [C++11:12.2/1]:
    类类型的临时变量是在各种上下文中创建的:将引用绑定到prvalue(8.5.3)、返回prvalue(6.6.3)、创建prvalue(4.1、5.2.9、5.2.11、5.4)的转换、引发异常(15.1)、输入处理程序(15.3)以及某些初始化(8.5)

    [C++11:6.6.3/2]:
    既没有表达式也没有大括号init列表的返回语句只能在不返回值的函数中使用,即返回类型为void的函数、构造函数(12.1)或析构函数(12.4)

    带有非void类型表达式的return语句只能在返回值的函数中使用;表达式的值将返回给函数的调用方。表达式的值将隐式转换为其出现的函数的返回类型。return语句可能涉及构造和cop临时对象的y或移动(12.2)。[注:在选择构造函数(12.8)时,为了解决重载问题,与返回语句相关联的复制或移动操作可能被省略或视为右值。-结束注]带有大括号init list的return语句通过从指定的初始化器列表复制列表初始化(8.5.4)来初始化要从函数返回的对象或引用。[示例:

    std::对f(常量字符*p,整数x){
    返回{p,x};
    }
    
    -[结束示例]

    此外,即使我们将以下解释为执行了新参考“对象”的初始化,裁判员当时可能仍然活着:

    [C++11:8.5.3/2]:
    初始化后不能将引用更改为引用另一个对象。请注意,对引用的初始化处理与对其赋值的处理非常不同。参数传递(5.2.2)和函数值返回(6.6.3)是初始化。

    • 这使得#1有效
    但是,您在
    main
    内部初始化一个新的参考
    ref
    std::reference_wrapper<T> r;
    
    {
        T t;
        r = std::ref(t);
    }
    
    // #1
    
    std::pair<std::string,int> f(const char* p, int x) {
       return {p,x};
    }