我正在尝试用户定义的文字(我相信在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
答案 0 :(得分:4)
您的操作员签名不正常,它应该返回一个新的临时值而不是LHS的引用。你实际上实现了+ =。
这就是你的plus运算符将绑定到非const引用的原因。
虽然您无法将临时绑定到非const引用,但您可以在其中执行非const操作。因此,当第一个术语实际上是临时术语时,它将编译你的+运算符。
即使vector<int>().swap( myVec )
是非const函数,它也是允许你myVec
从swap()
清除内存的相同构造。
如果要在单行中创建字符串构建函数,并且对象下方使用成员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+
。