Search code examples
c++templatesc++17variadic-templates

Storing and passing on parameter pack in factory class


I'm trying to write a "factory" class template whose instantiations have variadic constructors which store their arguments in a tuple and later pass on these arguments to the constructors of objects created by the factory.

A minimal example might make it clearer:

#include <memory>
#include <tuple>

struct Foo
{
  Foo(int arg1, double arg2)
  {}

  // ...
};

template<typename T, typename ...ARGS>
class Factory
{
public:
  Factory(ARGS&&... args)
  : _stored_args(std::make_tuple(std::forward<ARGS>(args)...))
  {}

  std::unique_ptr<T> create()
  { return std::apply(std::make_unique<T>, _stored_args); }

private:
  std::tuple<ARGS...> _stored_args;
};

template<typename T, typename ...ARGS>
std::unique_ptr<Factory<T, ARGS...>> make_factory(ARGS&&... args)
{ return std::make_unique<Factory<T, ARGS...>>(std::forward<ARGS>(args)...); }

int main()
{
  auto foo_factory(make_factory<Foo>(1, 2.0));

  auto foo_ptr(foo_factory->create());

  // ...
}

My problem here is that the call to std::apply is apparently malformed since both gcc and clang complain along the lines of no matching function for call to '__invoke'. What am I doing wrong here?


Solution

  • All you need to do is wrap the std::make_unique call into a perfect-forwarding lambda:

    std::unique_ptr<T> create() {
    return std::apply(
        [](auto&&... xs) {
            return std::make_unique<T>(std::forward<decltype(xs)>(xs)...);
        },
        _stored_args);
    }
    

    live example on godbolt.org

    The reason is that std::make_unique doesn't only take a T template argument, but also Args..., which in this case are deduced by fowarding xs.... See cppreference.