【问题标题】:Temporary objects in C++C++ 中的临时对象
【发布时间】:2013-10-18 18:29:54
【问题描述】:

我正在通过引用返回并遇到临时对象。我不明白如何识别它们。请用这个例子解释:

如果ab 是同一类的对象,请考虑二进制operator+。如果在f(a+b) 等表达式中使用它,那么a+b 将成为临时对象,而f 必须是f(const <class name>&)f(<class name>) 的形式。它不能是f(<class name>&) 的形式,但是(a+b).g() 完全可以,g() 甚至可以更改a+b 返回的对象的内容。

【问题讨论】:

  • 显示代码总比喋喋不休...
  • 您可能想让问题更清楚,也许通过显示一段代码来显示问题。
  • 我认为问题基本上是“为什么表达式 f(a+b) 会导致临时对象,而 (a+b).g() 不会?还有什么会导致表达式产生临时对象?”
  • @g-makulik 根据我刚刚添加的对问题的理解,问题是提问者不知道涉及什么代码。您如何确定哪些类型的代码会产生临时对象?如果问题是“涉及哪些类型的代码”,提问者可以提供什么代码?
  • @BlueMonkMN 太好了!我也大致了解了这一点,但只是认为出于教学原因强制 OP 发布真实代码会更好。

标签: c++ reference return


【解决方案1】:

当您说f(a + b) 时,f 的参数需要绑定到调用函数的值,并且由于该值是右值(即具有非引用返回类型的函数调用的值)*,参数类型必须是 const-lvalue-reference、rvalue-reference 或 non-reference。

相比之下,当你说(a + b).g()时,临时对象被用作成员函数调用中的隐式实例参数,它不关心值类别。可变值绑定到非 const 和 const 成员函数,而 const 值仅绑定到 const 成员函数(volatile 也是如此)。

实际上,C++11确实添加了一种方法来限定隐式实例参数的值类别,如下所示:

struct Foo()
{
    Foo operator+(Foo const & lhs, Foo const & rhs);

    void g() &;     // #1, instance must be an lvalue
    void g() &&;    // #2, instance must be an rvalue
}

Foo a, b;

a.g();            // calls #1
b.g();            // calls #1
(a + b).g();      // calls #2

*) 对于本例中的重载运算符就是这种情况,对于内置的二元运算符也是如此。您当然可以创建产生左值的重载运算符,尽管违反常见约定可能会被认为非常令人困惑。

【讨论】:

  • 请原谅我的无知,但什么是“prvalue”?错字,或者实际上是一个新术语?
  • @quetzalcoatl 这是C++11中引入的一个术语,意思是纯右值cppreference 有一些信息。
  • @DyP:我也将 OP 的声明视为他的 a + b 确实是一个右值的暗示。是的,我可能应该尝试做一个更普遍的真实陈述......我会在最后添加一个注释。
  • 由于(a+b)(如原始问题所示)可以表现为右值或左值,究竟是什么决定了调用哪个g?我错过了什么吗?
  • @BlueMonkMN:你的重载运算符的定义决定了它的返回值的值类别。我将 OP 的断言作为他的操作员“照常”行为的暗示,即产生一个右值。
【解决方案2】:

您的困惑不是因为您无法识别临时对象,在这两种情况下a+b 的结果都是临时对象,而是错误地假设非 const 方法需要左值并且不接受临时对象,这是不正确的。

【讨论】:

  • 如果方法f(T& t)修改了t并且a + b作为t传递给f,它将调用UB。大多数编译器会捕获它并阻止它编译,除非定义了 f(const T& t) 以便可以正确调用 f(a + b)
【解决方案3】:

对于一个简单的案例,请考虑以下代码:

int func(int lhs, int rhs)
{
    return lhs + rhs;
}

int main() {
    int a = 1, b = 2, c = 3;
    return func(a * c, b * c);
}

因为 func 需要两个整数,所以程序必须计算 a * cb * c 的值并将它们存储在某个地方——它不能将它们存储在 abc 中。所以生成的代码等价于:

int lhsParam = a * c;
int rhsParam = b * c;
return func(lhsParam, rhsParam);

同样,在func() 的末尾,我们返回一个计算值lhs + rhs。编译器必须将其存储在新的位置。

对于整数等,这似乎很简单,但请考虑

int function(std::string filename);
function("hello");

filename 必须是 std::string,但您传递了 const char*。那么编译器的作用是:

std::string filenameParam = "hello"; // construct a new object
function(filenameParam);

就像前面的示例一样,但这次希望更清楚的是我们正在构建一个临时对象。

注意:称它们为“somethingParam”的约定只是为了在此答案中清晰。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-24
    • 2016-01-30
    • 1970-01-01
    • 1970-01-01
    • 2013-02-14
    • 1970-01-01
    • 2019-07-11
    • 1970-01-01
    相关资源
    最近更新 更多