【问题标题】:What's the deal with temporary objects and references?临时对象和引用是怎么回事?
【发布时间】:2026-01-04 06:25:01
【问题描述】:

人们经常会读到你不能将普通的左值引用绑定到临时对象。因此,当他们不想涉及复制时,经常会看到 A 类的方法以 const A& 作为参数。然而这样的结构是完全合法的:

double& d = 3 + 4;

因为它不会将临时对象 3 + 4 绑定到引用 d,而是使用对象 3 + 4 初始化引用。正如标准所说,只有当值不是类型或引用(或继承)时,引用才会赢' 不使用使用转换或某事物从临时对象获得的对象(即另一个临时对象)进行初始化。在这种情况下你可以看到:

int i = 2;
double & d = i;

这是不合法的,因为 i 不是 double 类型,也不是从它继承的。然而这意味着,临时对象可以绑定到引用 - 但它真的有约束力吗?不是用临时对象作为参数的复制构造函数创建一个新对象吗?

因此,我认为,让方法采用 const A& 参数而不是 A& 并不是在第二种情况下,这种方法不能作为 A 类型的临时对象的参数(因为它会),而是因为它涉及复制构造函数(就像参数是 A 类型一样)。我说的对吗?

【问题讨论】:

  • double& d = 3 + 4;合法的,因为引用类型不是 const 限定的。
  • 只有 double&& d = 3 + 4; 是合法的(在 C++0x 中),因为 3+4 是一个右值(它是否有意义,合法与否,是一个不同的问题)。
  • 很难说你的问题到底是什么......

标签: c++ reference lvalue


【解决方案1】:

首先,正如其他人所说, 双& d = 3 + 4; 是合法的 C++;如果您的编译器接受它并声称 正在编译 C++,这是编译器中的错误。 (注意 大多数 C++ 编译器不声称可以编译 C++,除非你给出 它们是特殊选项,-std=c++98 在 g++ 的情况下,用于 例子。)

其次,这条规则的动机来自经验。 考虑以下示例:

void
incr( int& i )
{
    ++ i;
}

unsigned x = 2;
incr( x );  //  Implicit conversion of unsigned to int
            //  creates a temporary.
std::cout << x << std::endl;
            //  and x is still equal 2 here.

引用的原始实现没有这个 限制;您可以使用 a 初始化任何引用 暂时的。实际经验表明这太容易出错了, 所以需要引用 const 的限制是 介绍了。 (大约在 1988 年或 1989 年,所以今天没有任何借口 编译器不强制执行它。)

还要注意,人们经常听到将临时对象绑定到 const 引用延长了临时的生命周期。这是非常 误导:使用临时来初始化引用扩展 临时的生命周期(有某些例外),但 如果此引用用于初始化,则生命周期不会延长 其他引用,即使临时也绑定到 那些参考资料。

【讨论】:

    【解决方案2】:

    如果您担心函数参数列表中const &amp;&amp; 的含义和用途,我担心您找错了树,因为它与临时对象关系不大。

    void method( Object x );
    

    这确实从实际参数复制构造一个Object。当函数终止时,函数内对x 所做的任何更改都将丢失,函数的参数不会更改。

    但您不想支付复制构建的成本。

    void method( Object & x );
    

    从实际参数复制构造一个Object,但x 引用参数,即在函数内对x所做的任何更改对论点本身确实做了。

    但您不希望方法的调用者想知道他们的参数可能会发生什么。

    void method( const Object & x );
    

    不是从实际参数复制构造一个Object并且x不能在函数内更改

    您无需为复制构造函数付费,并且向调用者明确说明他的参数不会被篡改。

    您不能将临时对象作为参数传递给第二个变体(请参阅 unapersson 的答案),因为没有可更改的对象可以引用,但是因为该函数大声预示它将修改参数(因为它被声明为非常量引用),无论如何传递一个临时参数是没有意义的。

    【讨论】:

      【解决方案3】:

      double&amp; d = 3 + 4; 完全合法,事实上它不会被符合标准的编译器接受。 3+4 的结果是 int 类型的临时值 - 因此它只能绑定到 const 引用。

      绑定引用是真正的绑定。不涉及复制。它只是延长临时对象的生命周期。

      【讨论】:

        【解决方案4】:
        void f( int & n ) {
        }
        
        int main() {
            f( 1 + 2 );
        }
        

        不涉及复制构造,但临时不会被绑定。 g++ 的错误是:

        非常量初始化无效 'int&' 类型的引用来自 'int'类型的右值

        【讨论】:

          【解决方案5】:

          引用可以由相同类型的左值初始化,并且 3 + 4 不是左值。所以这是不合法的。 MS VC++ 编译器允许执行与您的对象类似的操作,但这不是标准的。

          【讨论】:

            最近更新 更多