R值参考上的重载和代码重复
|
考虑以下:
struct vec
{
int v[3];
vec() : v() {};
vec(int x, int y, int z) : v{x,y,z} {};
vec(const vec& that) = default;
vec& operator=(const vec& that) = default;
~vec() = default;
vec& operator+=(const vec& that)
{
v[0] += that.v[0];
v[1] += that.v[1];
v[2] += that.v[2];
return *this;
}
};
vec operator+(const vec& lhs, const vec& rhs)
{
return vec(lhs.v[0] + rhs.v[0], lhs.v[1] + rhs.v[1], lhs.v[2] + rhs.v[2]);
}
vec&& operator+(vec&& lhs, const vec& rhs)
{
return move(lhs += rhs);
}
vec&& operator+(const vec& lhs, vec&& rhs)
{
return move(rhs += lhs);
}
vec&& operator+(vec&& lhs, vec&& rhs)
{
return move(lhs += rhs);
}
多亏了r值引用,借助operator +的这四个重载,我可以通过重用临时对象来最大程度地减少创建的对象数量。但是我不喜欢本文介绍的重复代码。我可以减少重复次数来达到相同目的吗?
没有找到相关结果
已邀请:
4 个回复
誓猎贰
是哪种类型。 安全问题。考虑如下代码:
如果最后一个运算符返回右值引用,则
将是悬空引用。否则,不会。这不是人为的例子。例如,在内部for-range循环中使用
技巧以避免不必要的复制。但是由于引用绑定期间临时对象的生命周期扩展规则不适用于仅返回引用的函数调用,因此您将获得悬挂的引用。
如果最后一个运算符+返回对在第一次串联期间创建的临时目录的右值引用,则此代码将调用未定义的行为,因为字符串临时目录将不会存在足够长的时间。 在通用代码中,返回右值引用的函数迫使您编写
代替
仅仅因为最后一个op +可能返回右值引用。以我的拙见,这越来越难看。 由于类型
既“扁平”又小,因此这些op +重载几乎没有用。请参阅FredOverflow的答案。 结论:应该避免使用带有右值引用返回类型的函数,特别是如果这些引用可能引用短暂的临时对象。
和
是此经验法则的特殊用途例外。
炬卤遁蝎变
类型是\“ flat \”(没有外部数据),因此移动和复制的作用完全相同。因此,所有右值引用和
绝对不会给您任何性能上的好处。 我将摆脱所有其他重载,而只需编写经典的引用到const版本:
如果您对移动语义还不太了解,我建议您研究此问题。 多亏了r值引用,借助operator +的这四个重载,我可以通过重用临时对象来最大程度地减少创建的对象数量。 除少数例外,返回右值引用是一个非常糟糕的主意,因为此类函数的调用是xvalues而不是prvalues,并且您可能会遇到讨厌的临时对象生存期问题。不要做。
赐黄
这就是您将在99%的时间停止的地方。 (我可能会低估这个数字。)该答案的其余部分仅在您知道(例如通过使用探查器)值得进一步优化op +的额外副本后才适用。 为了完全避免所有可能的复制/移动,您确实需要这些重载:
尽管您经常需要使用多种类型的op +,但是宏或CRTP可能会为您生成它,因此您处于正确的轨道,不可能真正减少(AFAICT)。唯一的实际差异(我在上面对单独语句的偏爱是次要的)是您在operator +(const vec&lhs,vec && rhs)中添加两个左值时的副本:
通过CRTP减少重复
现在,不再需要为vec专门定义任何op +。 Addable可用于任何带有op + =的类型。
嫩昧竞莫
我像这样修复此错误:
运行示例输出:
接下来,我尝试了一种C ++ 03方法:
运行该程序完全没有输出。 这些是我使用clang ++获得的结果。向他们解释您的情况。您的里程可能会有所不同。