Построить кортеж из гетерогенного списка инициализаторов при вызове функции

Рассмотрим следующую функцию

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;
}

Можно ли каким-либо образом в С++ 17 изменить подпись функции так, чтобы строка с надписью "Fails" работала? (сохраняя эту линию тем же).

Ответы

Ответ 1

Я предполагаю, что короткий ответ - это не.
Грубо говоря, { args... } не является кортежем, и вы подвергаетесь тем же проблемам вычета и конверсии, с которыми вы столкнулись в С++ 14.


Как сказано, в С++ 14/17 вы можете сделать это, чтобы имитировать его (минимальный рабочий пример):

#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 делает магию для вас, и у вас есть что-то похожее на то, что вы хотите, с дополнительным уровнем косвенности (что обычно помогает решить любую проблему).


В С++ 17 вы также можете сделать это:

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

Это имеет типы аргументов, выведенные непосредственно из вызова конструктору, вместо того, чтобы явно указывать их. С помощью псевдонима вы даже можете пойти дальше и уменьшить выражения в точке вызова на что-то вроде этого:

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

В любом случае вы жертвуете удобочитаемостью для этого, и это не стоит с моей точки зрения.

Ответ 2

В С++ 17 вы можете написать:

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

До этого вы можете использовать std::make_tuple:

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

но ничего не позволяет линии немодифицировать и сохранять шаблон функции.