【问题标题】:Operator overloading in variadic template inheritance可变参数模板继承中的运算符重载
【发布时间】:2018-11-28 19:29:46
【问题描述】:

想象一下这样的代码:

struct Foo
{
    int foo{0};
};

Foo operator+(const Foo& lhs, const Foo& rhs)
{
    Foo ret;
    ret.foo = lhs.foo + rhs.foo;
    return ret;
}

struct Bar
{
    int bar{0};
};

Bar operator+(const Bar& lhs, const Bar& rhs)
{
    Bar ret;
    ret.bar = lhs.bar + rhs.bar;
    return ret;
}

template<typename... Ts>
struct Fooz : public Ts...
{

};

template<typename... Ts>
Fooz<Ts...> operator+(const Fooz<Ts...>& lhs, const Fooz<Ts...>& rhs)
{
    // how can you call base class's operator+ here?
}

int main(int argc, char **argv)
{
    Fooz<Foo,Bar> fooz1{1,1}; // fooz1.foo == 1; fooz1.bar == 1;
    Fooz<Foo,Bar> fooz2{2,2}; // fooz2.foo == 2; fooz2.bar == 2;

    // auto fooz3 = fooz1 + fooz2 // fooz3.foo == 3; fooz3.bar == 3;
    return 0;
}

这里需要可变参数继承,因为我希望将基结构中的所有成员变量都继承到可变参数类(请参阅main)。

问题是:是否可以在FooBaroperator+ 函数中调用基础结构的operator+

感谢任何帮助!

【问题讨论】:

    标签: c++11 operator-overloading c++14 c++17 variadic-templates


    【解决方案1】:

    中,如果Fooz 是问题中的聚合类型,您可以复制-列表-初始化Fooz 到(列表)-初始化每个直接基类结果:

    template <typename... Ts>
    Fooz<Ts...> operator+(const Fooz<Ts...>& lhs, const Fooz<Ts...>& rhs)
    {    
        return { {static_cast<const Ts&>(lhs) + static_cast<const Ts&>(rhs)}... };
    }
    

    DEMO

    中,您还需要提供一个构造函数:

    Fooz(const Ts&... ts) : Ts{ts}... {}
    

    【讨论】:

    • 这太棒了!如果Fooz 也不是聚合类型,是否有类似的解决方案?
    • @lubgr 你需要一个用户提供的构造函数,它显式调用成员初始化器列表中基类的构造函数
    • @lubgr,如果你使用operator+=,你可以使用逗号折叠表达式:gcc.godbolt.org/z/wt8OlR,这样就不需要聚合初始化。 (仅限 C++17)
    • @Frank operator+= 和逗号折叠表达式很棒。
    【解决方案2】:

    由于该问题被标记为 C++11,作为@PiotrSkotnicki 的替代方案,值得一提的是,可变参数参数的古老递归剥离也可用于实现这一目标:

    template<typename T, typename... Rest>
    struct Aggregate_add_impl {
      static void add(T& dst, const T& lhs, const T& rhs) {
        // intentional no-op
      }  
    };
    
    template<typename T, typename U, typename... Rest>
    struct Aggregate_add_impl<T, U, Rest...> {
      static void add(T& dst, const T& lhs, const T& rhs) {
          U& dst_as_u = static_cast<U&>(dst);
          const U& l_as_u = static_cast<const U&>(lhs);
          const U& r_as_u = static_cast<const U&>(rhs);
    
          dst_as_u = l_as_u + r_as_u;
          Add_impl<T,Rest...>::add(dst, lhs, rhs);
      }
    };
    
    template <typename... Ts>
    Fooz<Ts...> operator+(const Fooz<Ts...>& lhs, const Fooz<Ts...>& rhs)
    {    
        Fooz<Ts...> ret;
        Aggregate_add_impl<Fooz<Ts...>, Ts...>::add(ret, lhs, rhs);
        return ret;
    }
    

    这还有一个好处是不需要Fooz 是可聚合构造的(但它必须是默认的或可复制构造的)。

    值得注意的是,以这种方式实现operator+=实际上要简单一些,所以如果您同时拥有++=,只需实现后者即可。

    【讨论】:

    • 谢谢@Frank。这对于使用旧编译器的人也非常有用!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-15
    • 2013-01-10
    • 2014-11-11
    • 1970-01-01
    • 1970-01-01
    • 2015-11-10
    • 1970-01-01
    相关资源
    最近更新 更多