在函数调用时从异构初始化列表构建元组

Vin*_*ent 17 c++ tuples initialization aggregate-initialization c++17

考虑以下功能

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;
}
Run Code Online (Sandbox Code Playgroud)

在C++ 17中是否有任何方法可以修改函数签名,以便标记为"Fails"的行可以工作?(保持该线相同).

sky*_*ack 12

我的猜测是简短的回答是否定的.
粗略地说,{ args... }这不是一个元组,你会遇到你在C++ 14中遇到的相同的演绎和转换问题.


话虽这么说,在C++ 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;
}
Run Code Online (Sandbox Code Playgroud)

通用lambdas为你做了魔法,你有一些类似于你想要的东西与额外的间接水平(这通常有助于解决任何问题).


在C++ 17中,您也可以这样做:

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

这就是直接从对构造函数的调用推断出的参数类型,而不是显式指定它们.使用别名,您甚至可以进一步将调用点处的表达式减少为:

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

无论如何,你为此牺牲了可读性,从我的角度来看它是不值得的.


Jar*_*d42 7

在C++ 17中,您可能会写:

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

之前,您可以使用std::make_tuple:

f(std::make_tuple(3, 3.5, "Hello World!"), std::make_tuple('a', std::string("b")));
Run Code Online (Sandbox Code Playgroud)

但没有什么可以保持线未经修改并保留功能模板.