6

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>?

Sergey
  • 7,985
  • 4
  • 48
  • 80

1 Answers1

10

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);
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Why is the second variant better? `std::move` is, basically, a cast to rvalue reference, so the semantics of both code snippets seems to be identical. – Sergey Nov 10 '16 at 09:09
  • @Sergey: The second one continue to forward argument, whereas the first one forces to know the tuple type, may modify `t` (as `print_t` with its pass by copy). – Jarod42 Nov 10 '16 at 11:01
  • 2
    @Sergey We assume `print_t` isn't the only function you want to call; and that a `std::string&&` parameter might be freely moved-from. You do not happen to move-from it in your implementation. Semantically, nobody should be relying on the `std::string` after it is moved-into a function call, however, barring strong guarantees (like `get` provides). – Yakk - Adam Nevraumont Nov 10 '16 at 12:28