【问题标题】:initializer_list and template type deductioninitializer_list 和模板类型推导
【发布时间】:2012-09-08 00:43:52
【问题描述】:

考虑函数:

template<typename T>
void printme(T&& t) {
  for (auto i : t)
    std::cout << i;
}

或任何其他需要一个带有 begin()/end() 参数的函数 - 启用类型。

为什么以下是非法的?

printme({'a', 'b', 'c'});

当所有这些都合法时:

printme(std::vector<char>({'a', 'b', 'c'}));
printme(std::string("abc"));
printme(std::array<char, 3> {'a', 'b', 'c'});

我们甚至可以这样写:

const auto il = {'a', 'b', 'c'};
printme(il);

或

printme<std::initializer_list<char>>({'a', 'b', 'c'});

【问题讨论】:

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


    【解决方案1】:

    您的第一行 printme({'a', 'b', 'c'}) 是非法的,因为无法推断模板参数 T。如果您明确指定模板参数,它将起作用,例如printme&lt;vector&lt;char&gt;&gt;({'a', 'b', 'c'}) 或 printme&lt;initializer_list&lt;char&gt;&gt;({'a', 'b', 'c'})。

    您列出的其他是合法的,因为参数具有明确定义的类型,因此可以很好地推导出模板参数T。

    您的带有auto 的sn-p 也有效,因为il 被认为是std::initializer_list&lt;char&gt; 类型,因此可以推导出printme() 的模板参数。


    这里唯一“有趣”的部分是auto 将选择std::initializer_list&lt;char&gt; 类型,但模板参数不会。这是因为 C++11 标准的 § 14.8.2.5/5 明确指出这是模板参数的非推导上下文:

    一个函数形参,其关联实参是一个初始化列表 (8.5.4),但该形参没有 std::initializer_list 或对可能有 cv 限定的 std::initializer_list 类型的引用。 [例子:

    template<class T> void g(T);
    g({1,2,3}); // error: no argument deduced for T
    

    ——结束示例]

    但是对于 auto,第 7.1.6.4/6 节明确支持 std::initializer_list&lt;&gt;

    如果初始化器是 braced-init-list (8.5.4),则为 std::initializer_list&lt;U&gt;。

    【讨论】:

    • +1 我学到了一些东西。将std::initialiser_list&lt;&gt; 提升为普通库函数之外的东西。
    • 只是为了完整性。这是解决问题的方法:pastebin.com/huEGwnDt
    • 我们知道为什么会出现这种情况吗?对我来说似乎很奇怪,如果我想允许模板函数(可能是基于范围的算法)采用初始化列表参数,我必须为 std::initializer_list 提供重载。
    • @JosephMansfield 我还没有找到明确的答案,但我怀疑这与用于统一初始化的大括号有关。调用g({1, 2, 3}) 也可以表示g(Foo(1, 2, 3)),其中Foo 是任何类,其构造函数采用三个整数。
    • @4ZM 如果不带参数调用,调用有歧义。
    【解决方案2】:

    您还可以重载该函数以显式采用 initializer_list 类型的参数。

    template<typename T>
    void printme(std::initializer_list<T> t) {
      for (auto i : t)
        std::cout << i;
    }
    

    【讨论】:

    • 当然,但这会使其他版本失败,例如printme(std::vector&lt;char&gt;({'a', 'b', 'c'}));。不幸的是,模板专业化在这里不起作用。
    • 哦,那太好了!谢谢你。我以为我已经尝试过了,但我错了。模板专业化在这里工作得很好。由于该函数可以完全以相同的方式实现,剩下的就是如何弄清楚如何让其中一个调用另一个......
    • 工作正常。您甚至可以通过为 initializer_list 实现完美转发来改进该解决方案,如下所示:pastebin.com/1ttGniBH?
    【解决方案3】:

    这在 § 14.8.2.5/5 中有明确规定

    一个函数参数,其关联参数是 初始化列表但参数没有 std::initializer_list 或对可能具有 cv 资格的引用 std::initializer_list 类型。 [ 例子:

    template<class T> void g(T);
    g({1,2,3}); // error: no argument deduced for T
    

    ——结束示例]

    要使其工作,您可以显式指定模板参数类型。

    printme<std::initializer_list<int>>( {1,2,3,4} );
    

    【讨论】:

      猜你喜欢
      • 2015-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-26
      相关资源
      最近更新 更多