Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build tuple from heterogeneous initializer list at function call

Consider the following function

template <class... T, class... U>
void f(std::tuple<T...> t, std::tuple<U...> u)
{
    std::cout << sizeof...(T) << " " << sizeof...(U) << std::endl;
}

int main(int argc, char* argv[]) 
{
    f({3, 3.5, "Hello World!"}, {'a', std::string("b")}); // Fails
    return 0;
}

Would there be any way in C++17 to modify the function signature so that the line marked "Fails" would work? (keeping that line the same).

like image 912
Vincent Avatar asked Jun 05 '17 04:06

Vincent


2 Answers

My guess is that the short answer is no.
Roughly speaking, { args... } isn't a tuple and you incur in the same deduction and conversion issues you were facing in C++14.


That being said, in C++14/17 you can do this to simulate it (minimal, working example):

#include<iostream>
#include<string>
#include<tuple>
#include<utility>

template <class... T, class... U>
void f_(std::tuple<T...> t, std::tuple<U...> u) {
    std::cout << sizeof...(T) << " " << sizeof...(U) << std::endl;
}

template<typename... T>
auto f(T... t) {
    return [tup{std::make_tuple(t...)}](auto... u) {
        f_(std::move(tup), std::make_tuple(u...));
    };
}

int main(int argc, char* argv[]) {
    f(3, 3.5, "Hello World!")('a', std::string("b"));
    return 0;
}

Generic lambdas do the magic for you and you have something similar to what you want with an extra level of indirection (that usually helps to solve any problem).


In C++17 you can also do this:

f(std::tuple{3, 3.5, "Hello World!"}, std::tuple{'a', std::string("b")});

That is having types of arguments deduced directly from the call to the constructor instead of explicitly specifying them. With an alias you can even go further and reduce the expressions at the call point to something like this:

f(T{3, 3.5, "Hello World!"}, T{'a', std::string("b")});

Anyway you sacrifice readability for that and it doesn't worth it from my point of view.

like image 148
skypjack Avatar answered Sep 27 '22 22:09

skypjack


In C++17, you might write:

f(std::tuple{3, 3.5, "Hello World!"}, std::tuple{'a', std::string("b")});

Prior, you may use std::make_tuple:

f(std::make_tuple(3, 3.5, "Hello World!"), std::make_tuple('a', std::string("b")));

but nothing to let the line unmodified and keeping the function template.

like image 33
Jarod42 Avatar answered Sep 28 '22 00:09

Jarod42