8

我们都知道这样的事情在 c++ 中是有效的:

const T &x = T();

尽管:

T &x = T();

不是。

最近的一个问题中,对话导致了这条规则。OP 发布了一些明显唤起 UB 的代码。但我希望它的修改版本可以工作(这是修改后的版本):

#include <iostream>
using namespace std;

class A {
public:
    A(int k) { _k = k; };
    int get() const { return _k; };
    int _k;
};

class B {
public:
    B(const A& a) : _a(a) {}
    void b() { cout << _a.get(); }
    const A& _a;
};

B* f() {
    return new B(A(10));
}

int main() {
    f()->b();
}

这会在某些机器上打印垃圾,在其他机器上打印 10 个......对我来说听起来像 UB :-)。但后来我想,好吧,A它基本上是一个美化的int东西,它初始化一个并阅读它。为什么不直接调用Aanint看看会发生什么:

#include <iostream>
using namespace std;

typedef int A;

class B {
public:
    B(const A& a) : _a(a) {}
    void b() { cout << _a; }
    const A& _a;
};

B* f() {
    return new B(A(10));
}

int main() {
    f()->b();
}

10每次都会打印。至少看起来const 引用规则对int版本有效,但对类版本无效。由于使用堆,它们都只是UB吗?我只是对这个int版本很幸运,因为编译看到了所有consts 并直接打印出 a10吗?我错过了规则的哪个方面?

4

3 回答 3

19

它只是表明通过“在编译器中尝试”来分析语言行为通常不会产生任何有用的结果。出于同样的原因,您的两个示例均无效。

只有当您将该临时对象用作 const 引用的直接初始化程序时,临时对象的生命周期才会延长——只有这样才能在引用和临时对象之间建立“生命周期”链接。

尝试将临时对象作为构造函数的参数传递并在构造函数内部附加 const 引用不会建立上述链接,也不会延长临时对象的生命周期。

另外,根据 C++ 标准,如果你这样做

struct S {
  const int &r;

  S() : r(5) {
    cout << r; // OK
  }
};

临时的生命周期只延长到构造函数的末尾。一旦构造函数完成,临时死掉,这意味着这个

S s;
cout << s.r; // Invalid

是无效的。

您的实验int只是“似乎有效”,纯粹是偶然的。

于 2010-06-22T23:23:36.350 回答
3

你刚刚走运了。将 B::b 更改为:

void b() {
    int i = rand();
    int j = rand();
    cout << _a << endl;
}

打印出随机数。

于 2010-06-22T23:26:18.370 回答
3

每次打印 10 个。

稍微修改一下main函数,就不会再打印10了:

int main()
{
    B* p = f();
    cout << "C++\n";   // prints C++
    p->b();            // prints 4077568
}

这个链接是如何建立在什么级别的?

参见 12.2 [class.temporary] §4 和 §5:

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

There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is [...]

The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except: [...]

A temporary bound to a reference parameter in a function call persists until the completion of the full-expression containing the call.

So in your case, the temporary is destroyed after the evaluation of the full-expression new B(A(10)).

于 2010-06-22T23:27:57.797 回答