将临时作为 const T& 传递并绑定到类引用的副作用

问题描述

假设我有一个结构定义和一些像这样的函数

struct A{
 const std::string& _s;
 A(const std::string& s):_s(s){}
 //A(const std::string s): _s(s){}
};
 int main(void){
    A a("E");
    std::cout << a._s << '\n';
}

认情况下,这根本不会出错或发出警告,您可以通过使用 asan 进行编译,使其在运行时抛出错误。根据我的理解,"E 绑定到的临时 s 在第一个表达式结束时死亡,这意味着 A a("E") 因此类中的引用在此之后无效。因此,在 cout << 点,我们正在访问一个悬空引用。

在编译时,如果我们将第一个构造函数替换为第二个构造函数,则会生成警告,它将引用绑定到临时 s,因此现在在 {{1} 之后访问 s } 表达式结束也是UB。

假设两种情况都是正确的,是否存在这样的情况

A a("E")

会是UB吗?如果是这样,什么时候?

解决方法

c++17 standard §15.2 点 6.9 说

  • 在函数调用 (8.5.1.2) 中绑定到引用参数的临时对象一直存在,直到包含调用的完整表达式完成

所以你很好。此处没有 UB auto x= A("E")._s

您自己描述的其他问题。建设完成后访问会员为UB。

但是,您可以通过将临时对象绑定到本地常量引用来延长其生命周期,如下所示:

struct A {
   const std::string& _s;
   A(const std::string& s) : _s(s) {}    
};

int main(void) {
   const std::string& e = "E";
   A a(e);
   std::cout << a._s << '\n';
}

这不会是 UB,但我不会像这样使用它。

,

以下是一些简单的测试代码,可准确打印各种对象的生命周期何时开始和停止:

struct loud {
    loud(const char*) { std::cout << __PRETTY_FUNCTION__  << '\n'; }
    loud(const loud&) { std::cout << __PRETTY_FUNCTION__  << '\n'; }
    ~loud() { std::cout << __PRETTY_FUNCTION__  << '\n'; }
};

struct A {
    const loud& _s;
    // A(const loud& s) : _s(s) {}
    A(loud s) : _s(s) {}
};

int main() {
    loud x = A("E")._s;
}

输出:

loud::loud(const char *)
loud::loud(const loud &)
loud::~loud()
loud::~loud()

并且如您所见,使用 loud 构造的 "E" 对象在复制之前不会被销毁。


在这两种情况下,都会创建一个临时对象并将其绑定到 const loud& sloud s(构造函数的参数)。所有临时对象在包含它们的完整表达式结束时被销毁,因此它们将在整个 A("E")._s 内保持活动状态,因此可以从中复制。

这也意味着以下内容不起作用,因为临时变量是在不同的完整表达式中创建的:

// The following is a hard compile time error on clang,but compiles on gcc
struct A {
    const loud& _s;
    A(loud s) : _s(s) {}
    A(const char* s) : _s(s)  /* Temporary `loud` used to initialize `_s` is destroyed here */ {}
};

// But this compiles
struct A {
    const loud& _s;
    A(loud s) : _s(s) {}
    A(const char* s) : A(loud(s))  /* Temporary materialized from `loud(s)` is destroyed here */ {}
};

临时对象在构造函数体的 { 之前被销毁(以及任何后续数据成员的构造函数之前,如果有的话),所以它已经在 . 之前被销毁了 {{ 1}}。

在这两种情况下,使用相同的 A("E")._s 函数输出以下内容:

main

(对象被销毁后使用的地方,如果复制构造函数试图访问对象上的任何数据成员,就像它是一个 loud::loud(const char *) loud::~loud() loud::loud(const loud &) loud::~loud()

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...