6

Imagine a code like this:

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

The variadic inheritance here is needed, since I want to have all the member variables from the base structs inherited to the variadic class (see main).

The question is: is it possible to call the base struct's operator+ inside FooBar's operator+ function?

Any help is appreciated!

0

2 Answers 2

5

In , if Fooz is an aggregate type as in the question, you can copy-list-initialize Fooz to (list)-initialize each direct base class with individual results:

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

In , you'd additionally need to provide a constructor:

Fooz(const Ts&... ts) : Ts{ts}... {}
Sign up to request clarification or add additional context in comments.

4 Comments

This is awesome! Is there a similar solution if Fooz is not an aggregate type, too?
@lubgr you would need a user-provided constructor that explicitly calls the constructors of base classes on a member initalizer list
@lubgr, if you work with operator+= instead, you could use a comma fold-expression: gcc.godbolt.org/z/wt8OlR, this way, no need for the aggregate initialization. (C++17 only)
@Frank That's great with the operator+= and comma fold-expression.
2

Since the question is tagged C++11, as an alternative to @PiotrSkotnicki, it's worth mentioning that good-old recursive peeling of the variadic parameters can also be used to achieve that:

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

This also has the benefit of not requiring Fooz to be aggregate constructible (but it DOES have to be default or copy-constructible).

It's worth noting that implementing operator+= this way is actually quite a bit simpler, so if you have both + and +=, just implement the later instead.

1 Comment

Thank you @Frank. This is also super useful for people with older compilers!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.