首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在添加新的c++0x右值引用运算符重载时减少冗余代码

如何在添加新的c++0x右值引用运算符重载时减少冗余代码
EN

Stack Overflow用户
提问于 2010-04-23 12:13:05
回答 4查看 1.5K关注 0票数 19

我正在添加新的操作符重载来利用c++0x右值引用,并且我感觉我正在生成许多冗余代码。

我有一个名为tree的类,它包含一个关于双精度值的代数运算树。下面是一个示例用例:

代码语言:javascript
复制
tree x = 1.23;
tree y = 8.19;
tree z = (x + y)/67.31 - 3.15*y;
...
std::cout << z; // prints "(1.23 + 8.19)/67.31 - 3.15*8.19"

对于每个二元运算(如加号),每个边可以是左值、右值treedouble。这会导致每个二进制操作产生8个重载:

代码语言:javascript
复制
// core rvalue overloads for plus:
tree operator +(const tree& a, const tree& b);
tree operator +(const tree& a, tree&&      b);
tree operator +(tree&&      a, const tree& b);
tree operator +(tree&&      a, tree&&      b);

// cast and forward cases:
tree operator +(const tree& a, double      b) { return a + tree(b); }
tree operator +(double      a, const tree& b) { return tree(a) + b; }
tree operator +(tree&&      a, double      b) { return std::move(a) + tree(b); }
tree operator +(double      a, tree&&      b) { return tree(a) + std::move(b); }

// 8 more overloads for minus

// 8 more overloads for multiply

// 8 more overloads for divide

// etc

对于每个二元运算(减法、乘法、除法等)也必须以某种方式重复。

正如您所看到的,实际上我只需要编写4个函数;其他4个函数可以转换并转发到核心用例。

您对减小此代码的大小有什么建议吗?

PS:这个类实际上比doubles树要复杂得多。减少副本确实可以极大地提高项目的性能。所以,rvalue重载对我来说是值得的,即使有额外的代码。我怀疑可能会有一种方法来消除上面的“抛出和转发”案例,但我似乎想不出任何东西。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-05-01 16:17:14

简单地说一下:如果所讨论的类是可移动的,那么移动成本非常低,并且如果可以,您总是可以从所有参数中移动,然后按值传递参数可能是一种选择:

代码语言:javascript
复制
tree operator +(tree      a, tree      b);

如果tree是可移动的,并且右值ref被作为实际参数传递,则函数的参数将在可能的情况下使用tree的移动构造函数初始化,否则使用复制构造函数初始化。然后,函数可以以适当的方式对其参数做任何它想做的事情(例如,移动它们的内部)。

与大量重载的版本相比,当传递一个右值引用参数时,它确实会引起额外的移动,但我认为它通常更好。

此外,IMO,tree &&参数可能会通过临时副本接受左值,但这不是任何编译器目前所做的,所以它不是很有用。

票数 7
EN

Stack Overflow用户

发布于 2010-04-23 12:26:52

首先,我根本不明白为什么operator+要修改参数(这不是一个典型的不可变的二叉树实现吗),所以在r值和l值引用之间没有区别。但是让我们假设子树有一个指向父树的指针或类似的东西。

从您展示的用法示例中,看起来有一个从双精度到树的隐式转换。在这种情况下,您的“转换和转发”情况是不需要的,编译器将找到用户定义的转换。

非移动重载不会最终生成一个新实例进入新树吗?如果是这样的话,我想你可以把剩下的四个案例中的三个作为货代来写。

代码语言:javascript
复制
tree operator +(tree&& a, tree&& b); // core case
tree operator +(tree   a, tree   b) { return std::move(a) + std::move(b); }
tree operator +(tree   a, tree&& b) { return std::move(a) + std::move(b); }
tree operator +(tree&& a, tree   b) { return std::move(a) + std::move(b); }

当然,您可以使用宏来帮助生成每个操作符的三个(或七个)转发版本。

EDIT:如果这些调用是不明确的,或者解析为递归,那么:

代码语言:javascript
复制
tree add_core(tree&& a, tree&& b);
tree operator +(tree&& a, tree&& b) { return add_core(std::move(a), std::move(b)); }
tree operator +(tree   a, tree   b) { return add_core(std::move(a), std::move(b)); }
tree operator +(tree   a, tree&& b) { return add_core(std::move(a), std::move(b)); }
tree operator +(tree&& a, tree   b) { return add_core(std::move(a), std::move(b)); }

EDIT:运算符使用隐式转换失败的再现:

代码语言:javascript
复制
#include <iostream>

template<typename T>
class tree;

template<typename T> tree<T> add(tree<T> a, tree<T> b)
{
    std::cout << "added!" << std::endl << std::endl;
    return tree<T>();
}

template<typename T> tree<T> operator +(tree<T>   a, tree<T>   b) { return add(a, b); }

template<typename T>
class tree
{
public:
    tree() { }
    tree(const tree& t) { std::cout << "copy!" << std::endl; }
    tree(double val)    { std::cout << "double" << std::endl; }
    friend tree operator +<T>(tree a, tree b);
};

int main()
{
    tree<double>(1.0) + 2.0;
    return 0;
}

和不带模板的版本,其中隐式转换有效:

代码语言:javascript
复制
#include <iostream>

class tree
{
public:
    tree() { }
    tree(const tree& t) { std::cout << "copy!" << std::endl; }
    tree(double val)    { std::cout << "double" << std::endl; }
friend tree operator +(tree a, tree b);
};

tree add(tree a, tree b)
{
    std::cout << "added!" << std::endl << std::endl;
    return tree();
}

tree operator +(tree a, tree b) { return add(a, b); }

int main()
{
    tree(1.0) + 2.0;
    return 0;
}
票数 4
EN

Stack Overflow用户

发布于 2010-04-27 02:08:44

你应该将它们定义为成员函数,这样你就不必重载左值或右值作为主要单元(无论如何这是不必要的),即,

代码语言:javascript
复制
class Tree {
    Tree operator+ const (const Tree&);
    Tree operator+ const (Tree&&);
};

因为第一个的l或r值是不相关的。此外,如果该构造函数可用,编译器将自动为您构造。如果树是从double构造的,那么您可以在这里自动使用double,double将是一个合适的右值。这只是两种方法。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2696156

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档