无法从大括号括起来的初始化列表转换为std元组

时间:2018-04-25 20:54:00

标签: c++ variadic-templates c++17 initializer-list uniform-initialization

作为更大项目的一部分,我正在玩std::tuple和模板;请考虑以下代码:

template <typename ...T> void foo(tuple<T...> t) {}
void bar(tuple<int, char> t) {}
tuple<int, char> quxx() { return {1, 'S'}; }

int main(int argc, char const *argv[])
{
    foo({1, 'S'});           // error
    foo(make_tuple(1, 'S')); // ok
    bar({1, 'S'});           // ok
    quxx();                  // ok
    return 0;
}

根据this answer C ++ 17支持来自 copy-list-initialization 的元组初始化,但是由于我收到以下错误(GCC 7.2.0),似乎这种支持是有限的:

main.cpp: In function 'int main(int, const char**)':
main.cpp:14:17: error: could not convert '{1, 'S'}' from '<brace-enclosed initializer list>' to 'std::tuple<>'
     foo({1, 'S'}); // error
                 ^

在这种情况下,我有什么方法可以使用大括号括起来的句法吗?

某些上下文:这将在运算符重载中使用,所以我想我已经绑定到元组并且无法使用可变参数,任何提示都被广泛接受。

额外:Clang 6也抱怨

prog.cc:12:5: error: no matching function for call to 'foo'
    foo({1, 'S'});           // error
    ^~~
prog.cc:6:31: note: candidate function [with T = <>] not viable: cannot convert initializer list argument to 'tuple<>'
template <typename ...T> void foo(tuple<T...> t) {}

2 个答案:

答案 0 :(得分:7)

braced-init-list ,如{1, 'S'},实际上并没有类型。在模板推导的上下文中,您只能在某些情况下使用它们 - 当针对initializer_list<T>进行推导时(其中T是函数模板参数)或者相应的参数已经由其他内容推断出来。在这种情况下,这两件事都不是真的 - 所以编译器无法弄清楚...T应该是什么。

所以你可以直接提供类型:

foo<int, char>({1, 'S'});

或者您可以自己构建tuple并将其传递给:

foo(std::tuple<int, char>(1, 'S')); // most explicit
foo(std::tuple(1, 'S')); // via class template argument deduction

今天,ClassTemplate<Ts...>只能从ClassTemplate<Us...>类型的表达式或从类似内容继承的类型推断出来。假设的提议可以扩展到另外尝试对表达式执行类模板参数推断以查看该推导是否成功。在这种情况下,{1, 'S'}不是tuple<Ts...>,但tuple __var{1, 'S'}成功推断tuple<int, char>以便这样做。这样的提议还必须解决诸如......如果我们推断ClassTemplate<T, Ts...>或任何微小的变化,这不是类模板参数演绎所允许的内容(但很多人有时会这样做表示有兴趣能够做到)。

我今天不知道这样的建议。

答案 1 :(得分:1)

  

根据这个答案,C ++ 17支持从复制列表初始化开始的元组初始化,但是由于我收到以下错误,所以似乎这种支持是有限的

问题是另一个问题。

当您致电bar({1, 'S'})时,编译器知道bar()收到tuple<int, char>,因此1int'S'为{ {1}}。

参见另一个例子:如果你定义

char

你可以打电话

void baz (std::tuple<int> const &)
 { }

因为编译器知道baz(1); 收到baz()所以请std::tuple<int>初始化元组中的1

但是

int

编译器不知道template <typename ...T> void foo(tuple<T...> t) { } 类型;当你打电话

T...

哪些foo({1, 'S'}); 类型应该推断出编译器?

我至少看到两个假设:T...T = int, char;或T = std::pair<int, char>

编译器应遵循哪种假设?

我的意思是:如果你将T = std::tuple<int, char>传递给std::tuple,编译器会接受元组中的类型列表作为foo()的列表;但如果你传递别的东西,编译器必须推导出正确的T...;但在这种情况下,这种推论并不是唯一的。所以错误。