Search code examples
c++templatestuplesoperator-overloadingvariadic-templates

How to concatenate two object which are based on tuple


I have a class template which is based on a std::tuple and only consist of that tuple. How can I concatenate two object of this class template in order to have all the tuple member of both class as an object of the same class template. Using tuple_cat does not works, probably because the types are not tuple

template<typename...Ts>
class foo : private std::tuple<Ts...> {
public:
    foo(Ts...vs) : std::tuple<Ts...>(vs...){}
    //  .
    //  .
    //  .
};



template<typename...T1s, typename...T2s>
foo<T1s..., T2s...> operator+(const foo<T1s...>& foo1, const foo<T2s...>& foo2){
    //  return std::tuple_cat(foo1, foo2); // this does not work
    //  what should be here?
}

foo<int, float, const char*> obj1(1, 1.23, "string1");
foo<const char*, float, int> obj2("string2" , 1.23, 1);

foo<int, float, const char*, const char*, float, int> obj3 = obj1 + obj2;// I want to do this by overloading of operator +

Solution

  • Because you inherit privately, the std::tuple base is not accessible to operator+, so you need to fix that one way or another. I think the easiest way is to make operator+ a friend of foo. Then, with the addition of a (private) constructor from std::tuple, you can just use std::tuple_cat on the correct types (through references or static_cast):

    template<typename... Ts>
    class foo : private std::tuple<Ts...> {
        explicit foo(std::tuple<Ts...>&& tuple) : std::tuple<Ts...>(std::move(tuple)) {}
    
    public:
        foo(Ts...vs) : std::tuple<Ts...>(vs...){}
    
        template<typename... T1s, typename... T2s>
        friend foo<T1s..., T2s...> operator+(foo<T1s...> const& foo1, foo<T2s...> const& foo2);
    };
    
    template<typename... T1s, typename... T2s>
    foo<T1s..., T2s...> operator+(foo<T1s...> const& foo1, foo<T2s...> const& foo2) {
        std::tuple<T1s...> const& tuple1 = foo1;
        std::tuple<T2s...> const& tuple2 = foo2;
        return foo(std::tuple_cat(tuple1, tuple2));
    }
    

    Demo

    If you don't want the additional constructor, you can use std::apply to pass all the elements to the main constructor instead:

    template<typename... T1s, typename... T2s>
    foo<T1s..., T2s...> operator+(foo<T1s...> const& foo1, foo<T2s...> const& foo2) {
        std::tuple<T1s...> const& tuple1 = foo1;
        std::tuple<T2s...> const& tuple2 = foo2;
        auto make_foo = [](auto&&... arg) { return foo(std::move(arg)...); };
        return std::apply(make_foo, std::tuple_cat(tuple1, tuple2));
    }