如何为 operator return 值执行 RVO?
How to enforce RVO for operator return value?
如何在最后 3 个操作符中强制执行 RVO:
#include <iostream>
class Noisy {
private:
int m_value;
public:
Noisy(int value = 0): m_value(value)
{
std::cout << "Noisy(int)\n";
}
Noisy(const Noisy& other): m_value(other.m_value)
{
std::cout << "Noisy(const Noisy&)\n";
}
Noisy(Noisy&& other): m_value(other.m_value)
{
std::cout << "Noisy(Noisy&&)\n";
}
//~Noisy() {
// std::cout << "dtor\n";
//}
Noisy operator+(const Noisy& rhs) &
{
std::cout << "+(const Noisy&)&\n";
return Noisy(m_value + rhs.m_value);
}
Noisy operator+(Noisy&& rhs) &
{
std::cout << "+(Noisy&&)&\n";
rhs.m_value += m_value;
return rhs; //std::move(rhs);
}
Noisy operator+(const Noisy& rhs) &&
{
std::cout << "+(const Noisy&) &&\n";
this->m_value += rhs.m_value;
return *this; //std::move(*this);
}
Noisy operator+(Noisy&& rhs) &&
{
std::cout << "+(Noisy&&) &&\n";
this->m_value += rhs.m_value;
return *this; //std::move(*this);
}
};
int main()
{
Noisy a, b, c, d, e, f, g;
Noisy z = a + b + c + d + e + f + g;
return 0;
}
程序运行输出:
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
+(const Noisy&)&
Noisy(int)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&)
或者在最后三个运算符中明确使用 std::move
时:
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
+(const Noisy&)&
Noisy(int)
+(const Noisy&) &&
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&)
我不想在运算符中复制,像这样:
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
+(const Noisy&)&
Noisy(int)
+(const Noisy&) &&
+(const Noisy&) &&
+(const Noisy&) &&
+(const Noisy&) &&
+(const Noisy&) &&
目前我想到的唯一方法是 return 来自运营商的引用,但这显然会导致悬空引用。
我用新的 g++ 在 c++14 和 c++17 中编译。
更新
我知道在不违反规则的情况下无法强制编译器做我想做的事。
但是,是什么促使编译器在本地优化右值呢?
我想它可以在第一次添加时创建一个右值,在接下来的添加中修改它,然后分配给结果变量。
简答
使你的右值引用调用者 + 常量引用参数运算符 return 成为 std::move
从 *this
编辑出来的右值引用。
Noisy&& operator+(const Noisy& rhs) &&
{
std::cout << "+(const Noisy&)&&\n";
m_value += rhs.m_value;
return std::move(*this);
}
由于这是从右值引用中触发的,因此您可以随意滥用并移动 *this
到调用者,他们可以随心所欲地使用它。
例子
#include <iostream>
class Noisy {
private:
int m_value;
public:
Noisy(int value = 0) : m_value(value)
{
std::cout << "Noisy()\n";
}
Noisy(const Noisy& other) : m_value(other.m_value)
{
std::cout << "Noisy(const Noisy&)\n";
}
Noisy(Noisy&& other) : m_value(other.m_value)
{
std::cout << "Noisy(Noisy&&)\n";
other.m_value = -1;
}
~Noisy()
{
std::cout << "~Noisy() : " << m_value << '\n';
}
Noisy operator+(const Noisy& rhs) const &
{
std::cout << "+(const Noisy&)&\n";
return Noisy(m_value + rhs.m_value);
}
Noisy&& operator+(const Noisy& rhs) &&
{
std::cout << "+(const Noisy&)&&\n";
m_value += rhs.m_value;
return std::move(*this);
}
Noisy operator+(Noisy&& rhs) const
{
std::cout << "+(Noisy&&)\n";
rhs.m_value += m_value;
return std::move(rhs);
}
};
int main()
{
Noisy a, b, c, d, e, f, g;
std::cout << "========================\n";
Noisy z = a + b + c + d + e + f + g;
std::cout << "========================\n";
return 0;
}
输出
Noisy()
Noisy()
Noisy()
Noisy()
Noisy()
Noisy()
Noisy()
========================
+(const Noisy&)&
Noisy()
+(const Noisy&)&&
+(const Noisy&)&&
+(const Noisy&)&&
+(const Noisy&)&&
+(const Noisy&)&&
Noisy(Noisy&&)
~Noisy() : -1
========================
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
注意 a.operator+(b)
的初始构造后,生成的对象通过右值引用沿链向下传播。最后一个操作是移动构造到z
。 (因此析构函数报告中的 -1
)。
我准备了一篇很长的描述,说明您的运算符链接如何使我打算 post 完成这项工作,但最后变得相当混乱。我只想说这个:
Noisy z = a.operator+(b).operator+(c).operator+(....
是您正在做的事情,您需要获取链中 (b)
之后所有内容的 lhs 操作数,以将其右值引用推送到下一个调用。我展示的操作员将允许这样做。
祝你好运,希望我理解了你的目标。
如何在最后 3 个操作符中强制执行 RVO:
#include <iostream>
class Noisy {
private:
int m_value;
public:
Noisy(int value = 0): m_value(value)
{
std::cout << "Noisy(int)\n";
}
Noisy(const Noisy& other): m_value(other.m_value)
{
std::cout << "Noisy(const Noisy&)\n";
}
Noisy(Noisy&& other): m_value(other.m_value)
{
std::cout << "Noisy(Noisy&&)\n";
}
//~Noisy() {
// std::cout << "dtor\n";
//}
Noisy operator+(const Noisy& rhs) &
{
std::cout << "+(const Noisy&)&\n";
return Noisy(m_value + rhs.m_value);
}
Noisy operator+(Noisy&& rhs) &
{
std::cout << "+(Noisy&&)&\n";
rhs.m_value += m_value;
return rhs; //std::move(rhs);
}
Noisy operator+(const Noisy& rhs) &&
{
std::cout << "+(const Noisy&) &&\n";
this->m_value += rhs.m_value;
return *this; //std::move(*this);
}
Noisy operator+(Noisy&& rhs) &&
{
std::cout << "+(Noisy&&) &&\n";
this->m_value += rhs.m_value;
return *this; //std::move(*this);
}
};
int main()
{
Noisy a, b, c, d, e, f, g;
Noisy z = a + b + c + d + e + f + g;
return 0;
}
程序运行输出:
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
+(const Noisy&)&
Noisy(int)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&)
或者在最后三个运算符中明确使用 std::move
时:
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
+(const Noisy&)&
Noisy(int)
+(const Noisy&) &&
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&)
我不想在运算符中复制,像这样:
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
+(const Noisy&)&
Noisy(int)
+(const Noisy&) &&
+(const Noisy&) &&
+(const Noisy&) &&
+(const Noisy&) &&
+(const Noisy&) &&
目前我想到的唯一方法是 return 来自运营商的引用,但这显然会导致悬空引用。
我用新的 g++ 在 c++14 和 c++17 中编译。
更新
我知道在不违反规则的情况下无法强制编译器做我想做的事。
但是,是什么促使编译器在本地优化右值呢?
我想它可以在第一次添加时创建一个右值,在接下来的添加中修改它,然后分配给结果变量。
简答
使你的右值引用调用者 + 常量引用参数运算符 return 成为 std::move
从 *this
编辑出来的右值引用。
Noisy&& operator+(const Noisy& rhs) &&
{
std::cout << "+(const Noisy&)&&\n";
m_value += rhs.m_value;
return std::move(*this);
}
由于这是从右值引用中触发的,因此您可以随意滥用并移动 *this
到调用者,他们可以随心所欲地使用它。
例子
#include <iostream>
class Noisy {
private:
int m_value;
public:
Noisy(int value = 0) : m_value(value)
{
std::cout << "Noisy()\n";
}
Noisy(const Noisy& other) : m_value(other.m_value)
{
std::cout << "Noisy(const Noisy&)\n";
}
Noisy(Noisy&& other) : m_value(other.m_value)
{
std::cout << "Noisy(Noisy&&)\n";
other.m_value = -1;
}
~Noisy()
{
std::cout << "~Noisy() : " << m_value << '\n';
}
Noisy operator+(const Noisy& rhs) const &
{
std::cout << "+(const Noisy&)&\n";
return Noisy(m_value + rhs.m_value);
}
Noisy&& operator+(const Noisy& rhs) &&
{
std::cout << "+(const Noisy&)&&\n";
m_value += rhs.m_value;
return std::move(*this);
}
Noisy operator+(Noisy&& rhs) const
{
std::cout << "+(Noisy&&)\n";
rhs.m_value += m_value;
return std::move(rhs);
}
};
int main()
{
Noisy a, b, c, d, e, f, g;
std::cout << "========================\n";
Noisy z = a + b + c + d + e + f + g;
std::cout << "========================\n";
return 0;
}
输出
Noisy()
Noisy()
Noisy()
Noisy()
Noisy()
Noisy()
Noisy()
========================
+(const Noisy&)&
Noisy()
+(const Noisy&)&&
+(const Noisy&)&&
+(const Noisy&)&&
+(const Noisy&)&&
+(const Noisy&)&&
Noisy(Noisy&&)
~Noisy() : -1
========================
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
注意 a.operator+(b)
的初始构造后,生成的对象通过右值引用沿链向下传播。最后一个操作是移动构造到z
。 (因此析构函数报告中的 -1
)。
我准备了一篇很长的描述,说明您的运算符链接如何使我打算 post 完成这项工作,但最后变得相当混乱。我只想说这个:
Noisy z = a.operator+(b).operator+(c).operator+(....
是您正在做的事情,您需要获取链中 (b)
之后所有内容的 lhs 操作数,以将其右值引用推送到下一个调用。我展示的操作员将允许这样做。
祝你好运,希望我理解了你的目标。