【问题标题】:Could not convert from brace-enclosed initializer list to std tuple无法从大括号括起来的初始值设定项列表转换为标准元组
【发布时间】:2018-04-25 20:54:00
【问题描述】:

作为更大项目的一部分,我正在使用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
                 ^

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

Some Context :这将在运算符重载中使用,所以我想我必须使用元组并且不能使用可变参数,任何提示都可以接受。

额外: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) {}

【问题讨论】:

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


    【解决方案1】:

    braced-init-list,如 {1, 'S'},实际上没有类型。在模板推导的上下文中,您只能在某些情况下使用它们——当针对initializer_list&lt;T&gt;(其中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&lt;Ts...&gt; 只能从 ClassTemplate&lt;Us...&gt; 类型的表达式或从类似的东西继承的类型推导出来。一个假设的提议可以将其扩展为额外尝试对表达式执行类模板参数推导,以查看该推导是否成功。在这种情况下,{1, 'S'} 不是tuple&lt;Ts...&gt;,但tuple __var{1, 'S'} 确实成功推断出tuple&lt;int, char&gt;,这样就可以了。这样的提议还必须解决诸如...如果我们要推断 ClassTemplate&lt;T, Ts...&gt; 或任何微小的变化怎么办,这不是类模板参数推导所允许的(但许多人有时表示感兴趣)能够做到)。

    我今天不知道有这样的提议。

    【讨论】:

    • 我们可以假设这会改变吗?对此有什么建议吗,也许是针对 C++2a 的?
    • @SamuelePilleri 不,不会。
    【解决方案2】:

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

    问题是另一个。

    当您调用bar({1, 'S'}) 时,编译器知道bar() 接收到tuple&lt;int, char&gt;,因此将1 作为int'S' 作为char

    看另一个例子:如果你定义

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

    你可以打电话

    baz(1);
    

    因为编译器知道baz() 接收std::tuple&lt;int&gt; 所以取1 来初始化元组中的int

    但是有

    template <typename ...T>
    void foo(tuple<T...> t)
     { }
    

    编译器不知道T... 类型;当你打电话时

    foo({1, 'S'}); 
    

    什么T... 类型应该推导出编译器?

    我至少看到了两个假设:T = int, charT = std::pair&lt;int, char&gt;;或者T = std::tuple&lt;int, char&gt;

    编译器应该遵循哪个假设?

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

    【讨论】:

    • 这就是骗我的!我的意思是,foo 需要 std::tuple,它应该非常简单。
    • @SamuelePilleri - 如果你传递了std::tuple,它会起作用;但是如果你传递别的东西,编译器必须推导出正确的元组;但在这种情况下,这种扣除并不是唯一的。所以错误。
    • 不能和类模板参数推导一样使用规则吗?
    • @SamuelePilleri - 我不知道;我想会有一些问题;无论如何,从std::tuple t({1, 'S'}),应该推导出哪个std::tuplestd::tuple&lt;std::pair&lt;int, char&gt;&gt;?还是std::tuple&lt;std::tuple&lt;int, char&gt;&gt;?仍然存在从{1, 'S'} 无法推断出唯一类型的问题。
    • @max66 想必如果我们有这个规则,就不会随意插入括号了吧?
    猜你喜欢
    • 1970-01-01
    • 2014-05-13
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    • 2011-05-31
    • 1970-01-01
    • 1970-01-01
    • 2018-09-06
    相关资源
    最近更新 更多