代码之家  ›  专栏  ›  技术社区  ›  Inverse

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

  •  19
  • Inverse  · 技术社区  · 16 年前

    我正在添加新的操作符重载以利用c++0x r值引用,我觉得我在生成大量冗余代码。

    tree

    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"
    

    树 ,R值 树 ,或 double . 这将导致每个二进制操作有8个重载:

    // 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个可以投射到核心案例。

    你对减少这段代码的大小有什么建议吗?

    附言: 离开模板

    4 回复  |  直到 16 年前
        1
  •  7
  •   Doug    16 年前

    只是一个简短的迟来的回答:如果所讨论的类是可移动的,那么移动是非常便宜的,并且如果可以,您总是从所有参数中移动,那么按值传递参数可能是一个选项:

    tree operator +(tree      a, tree      b);
    

    如果tree是可移动的,并且传递了一个rvalue ref作为实际参数,那么函数的参数将在可能的情况下用tree的move构造函数初始化,否则用copy构造函数初始化。然后,函数可以以适当的方式(比如说,移动它们的内部结构)使用它的参数做任何它想做的事情。

    与大量重载版本相比,它在传递rvalue引用参数时确实需要额外的移动,但我认为它通常更好。

    tree &&

        2
  •  4
  •   Ben Voigt    16 年前

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

    从您展示的使用示例来看,似乎有一个从double到tree的隐式转换。在这种情况下,不需要使用“cast and forward”用例,编译器将找到用户定义的转换。

    非移动重载不是最终生成一个新实例进入新树吗?如果是这样的话,我想你可以把剩下的四个箱子中的三个作为转发商。

    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); }
    

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

    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)); }
    

    编辑:重新显示未能使用隐式转换的运算符:

    #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;
    }
    

    #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;
    }
    
        3
  •  3
  •   Puppy    16 年前

    您应该将它们定义为成员函数,这样就不必将lvalue或rvalue作为主要单元进行重载(无论如何,这是不必要的),也就是说,

    class Tree {
        Tree operator+ const (const Tree&);
        Tree operator+ const (Tree&&);
    };
    

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

        4
  •  1
  •   Vicente Botet Escriba    16 年前

    tree operator +(const tree& a, const tree& b);
    

    r值和l值引用之间没有区别,所以您不需要同时定义

    tree operator +(tree&&      a, const tree& b);
    

    tree x = 1.23; 让我们想想,你不需要定义

    tree operator +(double      a, const tree& b){ return tree(a) + b; }
    

    如果运算符+按值获取树参数,则需要区分rvalues和lvalues

    tree operator +(tree a, tree b);