【问题标题】:Is it possible/legal to return reference to input temporary arguments which are passed by reference是否有可能/合法返回对通过引用传递的输入临时参数的引用
【发布时间】:2014-03-07 15:29:56
【问题描述】:

我的问题是:返回对通过引用传递的输入变量的引用是否合法。 我借用C++: Life span of temporary arguments?return by rvalue reference的例子

#include <iostream>

#include <string>

class MatrixClass
{
public:
int m_value;

std::string m_str;

MatrixClass(int a)
{
    m_value = a;
    std::cout << "hello: " << m_value << '\n';
}

MatrixClass(const MatrixClass& A)
{
    m_value = A.m_value;

    std::cout << "hello: " << m_value << '\n';

    if (A.m_str == "temp_in_*")
    {
        std::cout << "string: " << "copied from temp_in_*" << '\n';
    }

}

void operator=(const MatrixClass& A)
{
    m_value = A.m_value;

    std::cout << "hello: " << m_value << '\n';

    if (A.m_str == "temp_in_*")
    {
        std::cout << "string: " << "copied from temp_in_*" << '\n';
    }
}

~MatrixClass()
{
    std::cout << "bye: " << m_value << '\n';

    if (m_str == "temp_in_*")
    {
        std::cout << "string: " << "temp_in_*" << '\n';
    }
}

};

MatrixClass& operator+(MatrixClass& tempClassA, MatrixClass& tempClassB)
{
    tempClassA.m_value += tempClassB.m_value;
    return tempClassA;
}

MatrixClass operator*(MatrixClass& tempClassA, MatrixClass& tempClassB)
{
    MatrixClass Temp(101010);
    Temp.m_value = tempClassA.m_value * tempClassB.m_value;

    Temp.m_str = "temp_in_*";

    return Temp;
}

int main()
{
    MatrixClass A1(2);
    MatrixClass A2(3);
    MatrixClass A3(10);
    MatrixClass A4(11);
    MatrixClass A5(12);

    std::cout << "start of expression " << '\n';

    MatrixClass A6(0);

    A6 = A1 * A2 + A3 * A4 + A5 * A6;

    std::cout << "end of expression " << '\n';

    std::cout << "A6.m_value: " << A6.m_value << '\n';

    std::system("pause");
}

运算符返回对其输入的引用,该引用是一个临时变量,并通过 它给另一个运算符: operator*(A1, A2) 返回一个临时变量,也是 operator*(A3, A4), operator*(A5, A6)

临时变量的生命周期有什么问题吗?我正在开发一个矩阵类。

如果表达式更复杂会发生什么,例如:

    (A+B*C)*((A*B + C)*A)

一般问题是(取自return by rvalue reference

这可能吗:

改变

A compute(A&& v)
{
  (do something to v) 

  return v;
}

A& compute(A& v)
{
  (do something to v) 
  return v;
}

【问题讨论】:

  • 我知道这只是一个示例,但是您是否尝试过编译和运行该代码?除了一些语法错误之外,它从不复制任何对象,也从不创建任何临时变量。
  • operator* 的用法相当奇怪,因为您实际上实现了operator*=。只需正确定义operator*(即返回一个对象,而不是一个引用)。
  • 感谢斯特凡。 operator* 只是显示我的问题的一个示例:返回对通过引用传递的输入变量的引用是否合法
  • @liangbright:是的,这就是你实际实现operator*=operator&lt;&lt; 的方式。但是,您不应该做的是返回对临时对象的引用。
  • @stefan 谢谢。我更新了示例并在 vs2013 中进行了测试。尽管临时对象是通过引用传递的,但它给出了正确的答案。在我真正的 Matrix 类中,我确实需要创建一些临时对象。我可以按值从函数中返回它们(使用 std::move),并通过引用将它们传递给函数。但是如果我通过引用传递并返回它们,则不会复制任何对象->节省时间。我在vs2013上测试了我的课程,很好。但不知道是c++11标准还是只有vs2013支持。

标签: c++11 object-lifetime


【解决方案1】:

是的,函数返回对输入参数的引用是合法的,因为它可以编译,并且在许多用途中它可以毫无问题地工作。在表达式中创建的任何临时对象的生命周期都是完整表达式或语句的生命周期,因此只要在表达式之外不使用引用,用法应该可以正常工作。不过,这有点冒险,因为调用者可能不知道函数所做的引用传播,并且当引用通过中间函数传递时,延长临时生命周期的特殊规则通常不适用。

您的示例涉及修改和返回对非const 左值实例的引用。通常,这些类型的使用比对const 的引用或对右值的引用更难陷入陷阱。对非const 左值的引用不能绑定到临时值(至少在没有经过一些圈套来欺骗编译器的情况下不能绑定),因此您通常必须传递一个名为的实际左值(非临时)变量(或其他长期存在的对象)放入其中。当该引用作为返回值从函数中传出时,它将引用传入的任何长寿命对象。(当然,如果您没有正确管理生命周期,您仍然会遇到麻烦,但至少在这种情况下,我们所说的生命周期通常不止一个语句/表达式。)

如果你通过你的函数传递右值引用,特别是如果它们在表达式树的某个地方被翻译成非const左值(这有点容易做到,因为语言,作为一种安全特性,将右值衰减为左值,只要它们绑定到一个名称),引用的临时性质可能会丢失,并且更容易意外地将引用绑定到一个长期存在的引用,这将超过它绑定到的临时(通常赢得'不要超出创建它的语句/完整表达式)。这就是为什么我通常倾向于按值而不是按引用返回(通常是传递)r 值。然后,编译器更加了解生命周期问题,并且使用通常更加万无一失。在许多情况下,编译器无论如何都可以省略移动构造,而当它不能时,移动通常很便宜。

【讨论】:

  • 谢谢。我猜你的建议是在 main() { T aaa;通过引用将 aaa 传递给函数:T& FuncA(T&):FuncA(aaa),并从 FuncA } 返回引用 (T&) 是合法的。但是将返回 T 类型的临时对象(未命名)的 T FuncB() 传递给 FuncA,因为 FuncA(FuncB()) 不好。解决方法是将 T& FuncA(T&) 更改为 T&& FuncA(T&&) 或 T FuncA(T&&)。这是你的建议吗?
  • 你的例子行不通。 FuncB() 返回一个右值,它不能绑定到非const 左值引用。 (试一试 --- 你应该得到一个编译错误。)如果 FuncA 改为:T const &amp;FuncA(T const &amp;t) { return t; },那么是的,你会遇到问题。 FuncA(FuncB()) 将返回对临时对象的引用,该临时对象的生命周期在当前语句的末尾结束,因此如果您尝试保存并在语句之外使用该引用,则会遇到 UB。解决方案取决于您要做什么,但T &amp;&amp;FuncA(T &amp;&amp;) 通常不会是这样;这对修复生命周期没有任何作用。
  • “对非 const 左值的引用不能绑定到临时值(至少在不经过一些圈套来欺骗编译器的情况下)” 这些“技巧”包括 MSVC 编译器的默认设置:允许它的语言扩展。不幸的是。
  • so, T FuncA(T&& A) { 修改A 如A=A+1; return std::move(A)} 是一个解决方案吗?那么我可以使用 FuncA(FuncB) 吗?这是借自stackoverflow.com/questions/13430831/return-by-rvalue-reference/…
  • @dyp 谢谢。但我真的需要使用 FuncA(FuncB()) 而不使用 auto A = FuncB() ;功能A(A);有什么解决办法吗?
猜你喜欢
  • 1970-01-01
  • 2013-11-29
  • 1970-01-01
  • 2019-03-02
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 2018-07-27
相关资源
最近更新 更多