C++;使用对正在定义的变量的引用 是下面的代码有效C++,根据标准(折扣…s)?< /P>

C++;使用对正在定义的变量的引用 是下面的代码有效C++,根据标准(折扣…s)?< /P>,c++,scope,language-lawyer,self-reference,object-lifetime,C++,Scope,Language Lawyer,Self Reference,Object Lifetime,大家都知道这个项目使用的GCC版本(4.1.2和3.2.3…甚至不要让我开始…)进行编译,但是应该吗 编辑:我添加了一些细节,例如关于f()在原始代码中的概念外观。基本上,它意味着在某些条件下初始化x。语法上是这样,但是如果您尝试这样做 #include <iostream> using namespace std; typedef int T; bool f(T& x) { return true; } int main() { T x = (f(x) ?

大家都知道这个项目使用的GCC版本(4.1.2和3.2.3…甚至不要让我开始…)进行编译,但是应该吗


编辑:我添加了一些细节,例如关于f()在原始代码中的概念外观。基本上,它意味着在某些条件下初始化x。

语法上是这样,但是如果您尝试这样做

#include <iostream>
using namespace std;

typedef int T;
bool f(T& x)
{
    return true;
}
int main()
{
    T x = (f(x) ? x : T());
    cout << x;
}
然后输出10。 在第一种情况下,声明对象
x
,编译器会指定一些伪任意值(因此您不会初始化它),而在第二种情况下,您会在声明后指定一个值(
T()
,即
0
),即初始化它

我认为你的问题与此类似:
它无疑应该编译,但可能有条件地导致未定义的行为。

  • 如果
    T
    是非基本类型,则未定义行为(如果已分配)
  • 如果
    T
    是基元类型,如果它是非本地的,则为定义良好的行为;如果在读取之前未指定,则为未定义的行为(字符类型除外,其中定义为提供未指定的值)

本标准的相关部分是第3.8节“对象寿命:

类型为
T
的对象的生存期从以下时间开始:

  • 获得T型的适当对齐和尺寸的存储,以及
  • 如果对象具有非平凡的初始化,其初始化已完成。
所以
x
的生存期还没有开始。在同一节中,我们找到了使用
x
进行管理的规则:

类似地,在对象的生存期开始之前,但在对象将占用的存储已分配之后,或者在对象的生存期结束之后,在对象占用的存储被重用或释放之前,可以使用引用原始对象的任何glvalue,但只能以有限的方式使用。对于正在建造或破坏的物体,见12.7。否则,此类glvalue指的是已分配的存储(3.7.4.2),使用glvalue的属性(不依赖于其值)是明确定义的。如果出现以下情况,则程序具有未定义的行为:

  • 将左值到右值的转换(4.1)应用于此类glvalue,
  • glvalue用于访问非静态数据成员或调用对象的非静态成员函数,或
  • glvalue绑定到对虚拟基类(8.5.3)的引用,或
  • glvalue用作动态_转换(5.2.7)的操作数或typeid的操作数
如果您的类型是非基本类型,那么尝试分配它实际上是对非静态成员函数
T::operator=
的调用。句号,根据案例2,这是未定义的行为

原语类型是在不调用成员函数的情况下分配的,所以现在让我们仔细看看第4.1节“左值到右值的转换”,看看左值到右值的转换何时会是未定义的行为:

当未赋值操作数或其子表达式(第5条)中发生左值到右值的转换时,不访问被引用对象中包含的值。在所有其他情况下,转换结果根据以下规则确定:

  • 如果
    T
    是(可能是cv限定的)
    std::nullptr\u T
    ,则结果是一个空指针常量(4.10)
  • 否则,如果
    T
    具有类类型,则转换副本将从glvalue初始化类型为
    T
    的临时值,并且转换的结果是临时值的prvalue
  • 否则,如果glvalue引用的对象包含无效指针值(3.7.4.2、3.7.4.3),则该行为是实现定义的
  • 否则,如果
    T
    是(可能是cv限定的)无符号字符类型(3.9.1),并且glvalue引用的对象包含不确定值(5.3.4、8.5、12.6.2),并且该对象没有自动存储持续时间,或者glvalue是一元
    &
    运算符的操作数,或者绑定到引用,结果是一个未指定的值。
  • 否则,如果glvalue引用的对象包含不确定值,则行为未定义。
  • 否则,glvalue指示的对象中包含的值就是prvalue结果
(请注意,这些规则反映了对即将到来的C++14标准的重写,以使其更易于理解,但我认为这里的行为没有实际变化)

在进行左值引用并将其传递给
f()
时,变量
x
有一个不确定的值。只要该变量具有基元类型,并且在读取之前赋值(读取是左值到右值的转换),代码就可以了

如果变量在读取之前未赋值,则效果取决于
t
。字符类型将导致代码执行并使用任意但合法的字符值。所有其他类型都会导致未定义的行为


1除非
x
具有静态存储持续时间,例如全局变量。在这种情况下,根据第3.6.2节非局部变量的初始化,执行前初始化为零:

具有静态存储持续时间(3.7.1)或线程存储持续时间(3.7.2)的变量应在进行任何其他初始化之前进行零初始化(8.5)

在这种静态存储持续时间的情况下,不可能运行到lval
#include <iostream>
using namespace std;

typedef int T;
bool f(T& x)
{
    return true;
}
int main()
{
    T x = (f(x) ? x : T());
    cout << x;
}
bool f(T& x)
{
    x = 10;
    return true;
}
 #include <stdio.h>

 struct A {
        A()           { printf("A::A() %p\n",            this);     }
        A(const A& a) { printf("A::A(const A&) %p %p\n", this, &a); }
        ~A()          { printf("A::~A() %p\n",           this);     }
 };

 int main()
 {
  A a=a;
 }
A::A(const A&) 0253FDD8 0253FDD8
A::~A() 0253FDD8
r = ...;