C++17 - variadic template arguments - call class method on return value from previous with templated argument

Viewed 139

I have this code:

struct Foo{
    int sum;

    Foo() : sum(0) {}
    Foo(int x) : sum(x) {}

    Foo bar(int x){
        return Foo(sum + 1);
    }

    Foo bar(std::vector<int> x){
        return Foo(sum + 1);
    }

    Foo bar(const char* str){
        return Foo(sum + 1);
    }
};


template <typename ... T>
int foo(T ... args){            
    Foo foo;

    //(foo.bar(args), ...);
    return foo.sum;
}

int main() {
   foo(1, 2, "hello", std::vector<int>());
   return 0;
}

I want to create the call hierarchy like this:

Foo foo;
foo = foo.bar(1);
foo = foo.bar(2);
foo = foo.bar("hello");

based on variadic templated arguments args.

I was looking at fold expressions, but that seems to be not working as I need. I can fold methods to be called separatelly, but cannot call the next one on the result from the previous one.

Is there any way how to do this with?

2 Answers

You can use a folding expression for that.

((foo = foo.bar(args)), ...);

We need an additional () around the assignement to make the compiler accept the syntax. I'm assuming this is what you want? I can't really tell what the code is supposed to do...

Edit: As pointer out by @AVH doing a left-fold or right-fold here will have an impact on the order. The right-fold above will call the member functions starting with the last parameter and work it's way backward. A left-fold is probably what you want, and will do the opposite.

(..., (foo = foo.bar(args)));

Edit 2: In the end it turns out that the comma operator enforces evaluation from left to right, so in this case it actually does not matter how we fold the expression. If we folded over something other then , it could matter.

Here is a generic for for-each-in-pack function:

template<class F, class...Args>
constexpr F for_each(F f, Args&&...args) {
  std::initializer_list<int>{((void)f(std::forward<Args>(args)), 0)...};
  return f;
}

It's taken from here. Use that together with a lambda:

template <typename ... T>
int foo(T&& ... args){            
    Foo foo;
    for_each([&](auto&& x){ foo = foo.bar(std::forward<decltype(x)>(x));}
           , std::forward<T>(args) ...>);
    return foo.sum;
}

That's basically the same solution as the accepted one, but with the function abstracted.

Related