从多个临时工具中创建参考

时间:2014-10-06 08:22:27

标签: c++ gcc c++11 mingw

我正在尝试用户定义的文字(我相信在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 << "Constructor\n";    
    }
    ~Point()
    {
        std::cout << "Destructor\n";
    }
    Point& operator=(Point p)
    {
        std::cout << "Assignment op\n";
        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 _x\n";
    Point p(x);
    return p;
}

Point operator "" _y(const char* l)
{
    float y = atof(l);
    std::cout<<"literal _y\n";
    Point p(0, y);
    return p;
}

Point operator "" _z(const char* l)
{
    float z = atof(l);
    std::cout<<"literal _z\n";
    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

我希望这些案例中的任何一个而不是第一个案例,所以我的问题是:第一种情况究竟发生了什么?

P.S。我使用MinGW和gcc 4.8.1。编译字符串:g++ --std=c++11 -Wall main.cpp -o out.exe

3 个答案:

答案 0 :(得分:4)

您的操作员签名不正常,它应该返回一个新的临时值而不是LHS的引用。你实际上实现了+ =。

这就是你的plus运算符将绑定到非const引用的原因。

虽然您无法将临时绑定到非const引用,但您可以在其中执行非const操作。因此,当第一个术语实际上是临时术语时,它将编译你的+运算符。

即使vector<int>().swap( myVec )是非const函数,它也是允许你myVecswap()清除内存的相同构造。

如果要在单行中创建字符串构建函数,并且对象下方使用成员operator<<上的ostringstream,它也可能非常有用。结果是安全的,因为在这种情况下你做的最后一次调用是str()调用,它返回的值不是成员引用。

在您的情况下,您还可以在实施中使用此构造到operator+

class Point
{
    public:
      Point & operator+=( const Point& ); // as you implemented +

      Point operator+( const Point & rhs ) const
      {
        return Point( *this ) += rhs;
      }
};

答案 1 :(得分:3)

表达式12_x创建一个临时对象,创建对临时对象的引用显然不起作用,当临时对象被破坏时引用的内容(一旦表达式完成就会被引用) ?

但是,只要引用变量在范围内,进行const引用将导致编译器延长临时对象的生命周期。

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


如Potatoswatter所述,右值参考应该与临时对象一起使用。这是C ++ 11中引入的一种新参考,用双&符号表示,如

Point&& p = 12_x;

答案 2 :(得分:3)

在这一行:

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

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

当你写:

const Point& p = 12_x;
根据{{​​1}},

导致12_x返回的临时生命周期延长,因此只有当该引用超出范围时才会调用析构函数,也就是说,此时调用{ {1}}。

PS:摘录自12.2/5 ([class.temporary])print()

  

有两种情况下,临时表在与完整表达结束时不同的点被销毁。 (...)第二个上下文是指引用绑定到临时的。 (...)[例子:

12.2/4
  

(...)绑定到引用12.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; T3生命周期结束时销毁,即在程序结束时销毁。 (...)

BTW此处的示例还提供了实现cr的正确方法(除非您甚至不需要它cr)。您在代码中编写的内容应为operator+