【问题标题】:Forwarding initializer list expressions转发初始化器列表表达式
【发布时间】:2015-02-06 17:04:29
【问题描述】:

初始化器列表表达式对于初始化 C++ 容器非常方便:

std::vector<int>({1, 2, 3})

...但似乎大括号括起来的初始化列表表达式,如{1,2,3}绑定到采用std::initializer_list&lt;int&gt; 的函数 - 它不会 似乎绑定到一个通用(转发)引用

template <class T>
void foo(T&& v)
{
  std::vector<int>(std::forward<T>(v));
}

int main()
{
  foo({1, 2, 3})
}

这个输出:

test2.cpp:11:6: note: template<class U> void foo(U&&)
test2.cpp:11:6: note:   template argument deduction/substitution failed:
test2.cpp:33:13: note:   couldn't deduce template parameter ‘U’

(这是 GCC 4.7.2 的结果。)

不幸的是,这意味着我们无法转发初始化列表表达式。既然这样做会很方便,我想问为什么这不起作用?为什么大括号括起来的初始化列表表达式不能绑定到转发引用?或者这是允许的,也许我的编译器太旧了?

【问题讨论】:

  • 转发initializer_list是没有用的:它已经有引用语义,并且它的元素是const,因此不可移动。
  • 它们是否可移动并不重要 - 通用转发引用不一定意味着移动,它只是意味着按原样沿参数转发
  • 是的,但是花括号初始化列表没有类型。因此,您不能“按原样”转发它。
  • 括号初始化列表不是表达式。因此它没有类型。这一事实造成了无穷无尽的混乱。

标签: c++ templates c++11 initializer-list


【解决方案1】:

不是不能绑定到你函数的参数;只是编译器无法检测到模板的类型。这样编译:

#include <vector>

template <class T>
void foo(T&& v)
{
  std::vector<int>(std::forward<T>(v));
}

int main()
{
  foo(std::initializer_list<int>{1, 2, 3});
}

【讨论】:

    【解决方案2】:

    在这种情况下无法推导出初始值设定项列表。这实际上已被 [temp.deduct.call] 中的标准明确涵盖:

    模板参数推导是通过比较每个函数模板参数类型(称为P)与 调用的相应参数的类型(称为A),如下所述。如果P 是依赖类型, [...]。否则,初始化列表参数会导致参数被认为是非推导的 上下文(14.8.2.5)。 [示例:

    template<class T> void f(std::initializer_list<T>);
    f({1,2,3}); // T deduced to int
    f({1,"asdf"}); // error: T deduced to both int and const char*
    
    template<class T> void g(T);
    g({1,2,3}); // error: no argument deduced for T
    

    g 的示例正是您的情况 - T 不是依赖类型,因此这被认为是非推导上下文。编译器拒绝你的代码是正确的。

    【讨论】:

      猜你喜欢
      • 2011-10-12
      • 1970-01-01
      • 2016-04-27
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 2015-02-28
      • 2011-07-10
      相关资源
      最近更新 更多