c++c++14applyc++17stdapply

Applying a variadic function with std::apply


Is it possible to apply a variadic function to a tuple with std::apply?

For example, the following code works fine with GCC 6.2.1:

void print_t(std::string i, std::string j) {
    std::cout << i << " " << j << std::endl;
}

int main() {
        std::tuple<std::string, std::string> t{"ab", "cd"};
        std::experimental::apply(print_t, t);
        return 0;
}

But if I try to apply a variadic function:

template<typename T>
void vprint(T && t) {
    std::cout << std::forward<T>(t) << std::endl;
}

template<typename T, typename ... Ts>
void vprint(T && t, Ts ... ts) {
    std::cout << std::forward<T>(t) << " ";
    vprint<Ts...>(std::forward<Ts>(ts)...);
}

int main() {
        std::tuple<std::string, std::string> t{"fd", "ab"};
        std::experimental::apply(vprint, t);
        return 0;
}

the compiler complains that it cannot deduce template arguments of vprint. OK, let's write them explicitly:

std::experimental::apply(vprint<std::string, std::string>, t);

Now the compiler ends up with some obscure errors which expose standard library internals.

I wrote my own implementation of std::apply in C++11 and I understand why it can't deduce arguments of the variadic function template. But, in theory, std::apply has all the information needed for that deduction.

So is the application of variadic functions a not yet implemented feature in GCC6? Will C++17-compatible compilers allow such application? If not, will they allow application of instantiated variadic template functions, like vprint<std::string, std::string>?


Solution

  • With vprint<std::string, std::string>, you must pass r-value references, so

    std::experimental::apply(vprint<std::string, std::string>, std::move(t));
    

    The better way is to use functor (thanks to generic lambda):

    std::experimental::apply([](auto&&... args) {
                                 vprint(std::forward<decltype(args)>(args)...);
                             },
                             t);