Search code examples
c++c++17variadic-templatesstdtuple

How can I construct std::tuple of references from a parameter pack?


I have a builder class that I'd like to store arguments as references for use in subsequent building.

I'd like to pass in a variable number of arguments to my class, infer the template arguments using class template argument deduction, and store those passed arguments as references in a std::tuple.

What's the easiest way to convert from a parameter pack to a std::tuple of references?

I found std::forward_as_tuple which does something similar to what I want, but I don't want a forwarding reference plus it gives a syntax error during initialization of the member tuple.

template <typename... Ts>
struct Builder
{
using ArgsT = decltype(std::forward_as_tuple(Ts{}...));

ArgsT args_;

Builder(Ts&... ts) : args_(ts...) {}
};

int main()
{
    struct Foo
    {
        int a;
        double b;
    };

    Foo foo{};
    Builder fooBuilder{foo.a, foo.b};
}

The syntax error is:

error: no matching function for call to std::tuple<int&&, double&&>::tuple(int&, double&)


Solution

  • If you simply wants reference, use them directly:

    template <typename... Ts>
    struct Builder
    {
        using ArgsT = std::tuple<Ts&...>;
    
        ArgsT args_;
    
        Builder(Ts&... ts) : args_(ts...) {}
    };
    
    int main()
    {
        struct Foo
        {
            int a;
            double b;
        };
    
        Foo foo{};
        Builder fooBuilder{foo.a, foo.b};
    }
    

    For your code:

    decltype(std::forward_as_tuple(Ts{}...)) resolves in std::tuple<Ts&&...>.
    Ts{} creates a temporary (and requires your type to be default constructible).

    And you cannot bind int& to int&&.

    You might use decltype(std::forward_as_tuple(std::declval<Ts&>()...)) which resolves in std::tuple<Ts&...>, but later is simpler and provided solution;-).