从多个临时文件中创建引用

Creating a reference out of multiple temporaries

本文关键字:创建 引用 临时文件      更新时间:2023-10-16

我正在尝试用户定义的文字(我相信是在gcc 4.7.1中引入的),但对临时性的工作方式感到困惑。

考虑以下代码:

#include <stdlib.h>
#include <iostream>
class Point
{
public:    
    Point(float x = 0, float y = 0, float z = 0)
    :_x(x), _y(y), _z(z) 
    {
        std::cout << "Constructorn";    
    }
    ~Point()
    {
        std::cout << "Destructorn";
    }
    Point& operator=(Point p)
    {
        std::cout << "Assignment opn";
        return *this;
    }
    Point& operator+(const Point& p)
    {
        std::cout << "Returning ref: operator+n";
       _x += p._x; 
       _y += p._y; 
       _z += p._z; 
       return *this;
    }
    void print() const
    {
        std::cout << "(" << _x << ", " << _y << ", " << _z <<")n";
    }
protected:
    float _x, _y, _z;
};
Point operator "" _x(const char* l)
{
    float x = atof(l);
    std::cout<<"literal _xn";
    Point p(x);
    return p;
}
Point operator "" _y(const char* l)
{
    float y = atof(l);
    std::cout<<"literal _yn";
    Point p(0, y);
    return p;
}
Point operator "" _z(const char* l)
{
    float z = atof(l);
    std::cout<<"literal _zn";
    Point p(0, 0, z);
    return p;
}
int main(int argc, char **argv)
{
    Point& p = 12_x + 2_x + 3_y + 4_z;
    p.print();
}

执行后,我得到以下输出:

literal _z
Constructor
literal _y
Constructor
literal _x
Constructor
literal _x
Constructor
Returning ref: operator+
Returning ref: operator+
Returning ref: operator+
Destructor
Destructor
Destructor
Destructor
(14, 3, 4)

当我将行更改为Point& p = 12_x;时,会发出一个错误,说明您无法从右值初始化引用。

当我将其更改为const Point& p = 12_x;时,我得到:

literal _x
Constructor
(12, 0, 0)
Destructor

我预计会出现这两种情况中的任何一种,而不是第一种,所以我的问题是:第一种情况到底会发生什么?

附言:我使用的是带有gcc 4.8.1的MinGW。编译字符串:g++ --std=c++11 -Wall main.cpp -o out.exe

您的+运算符签名不正常,它应该返回一个新的临时签名,而不是对LHS的引用。您实际上已经实现了+=。

这就是您的加号运算符将绑定到非常量引用的原因。

虽然不能将临时引用绑定到非常数引用,但可以对其执行非常数操作。因此,当第一个术语实际上是临时的时,它将编译您的+运算符。

即使swap()是一个非常数函数,它也可以使用vector<int>().swap( myVec )myVec中清除内存。

当您希望在一行中创建字符串构建函数,并且在对象下面使用成员ostringstream上的operator<<时,它也可以用于字符串构建函数。这是安全的,因为在这种情况下,您进行的最后一个调用是str()调用,它返回的值不是成员引用。

在您的情况下,您也可以在operator+ 的实现中使用此构造

class Point
{
    public:
      Point & operator+=( const Point& ); // as you implemented +
      Point operator+( const Point & rhs ) const
      {
        return Point( *this ) += rhs;
      }
};

在行中:

Point& p = 12_x + 2_x + 3_y + 4_yz;

您得到一个对临时对象的引用,该对象在执行该行后立即被销毁,从而导致未定义的行为。实际上,您可以在日志中看到:调用四个构造函数,然后调用四个析构函数——因此,在调用print()之前,所有创建的对象都会被销毁。

当你写:

const Point& p = 12_x;

根据12.2/5 ([class.temporary]),它会导致12_x返回的临时的生存期延长,因此只有当引用超出范围时,也就是说,这次是在调用print()之后,才会调用析构函数。

PS:摘录自12.2/412.2/5

有两种情况下,临时性在不同于完整表达结束时被破坏。(…)第二个上下文是引用绑定到临时的。(…)[示例:

struct S {
    S();
    S(int);
    friend S operator+(const S&, const S&);
    ~S();
};
S obj1;
const S& cr = S(16)+S(23);
S obj2;

(…)绑定到引用cr的临时T3cr的生命周期结束时,即在程序结束时被销毁。(…)

顺便说一句,这里的例子也提供了实现operator+的正确方法(除了你可能甚至不需要它是friend)。您在代码中所写的内容应该是operator+=

表达式12_x创建了一个临时对象,创建对临时对象的引用显然是不起作用的,当临时对象被破坏时会引用什么(一旦它所在的表达式完成,它就会是什么)?

但是,只要引用变量在作用域内,进行const引用就会导致编译器延长临时对象的生存期。

带有加法的较长表达式也会创建临时对象,但由于operator+函数被定义为返回引用,因此整个表达式的结果就是引用。不幸的是,它将是对临时对象的引用,使用它将导致未定义的行为,所以你很幸运它能工作。


正如Potatoswatter所指出的,一个重新值引用应该与临时对象一起使用。这是C++11中引入的一种新的引用,用双安培数表示,如

Point&& p = 12_x;