【问题标题】:C++11 Implicit conversion from initialization list to array parameterC++11 从初始化列表到数组参数的隐式转换
【发布时间】:2013-06-30 07:52:47
【问题描述】:

在 C++11 中,是否可以做类似以下的事情?

template<typename T, size_t N>
void foo(array<T, N> src) { ... }

...

foo({1, 2, 3})

我目前正在运行 GCC 4.8。

【问题讨论】:

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


    【解决方案1】:

    是的,我设法完成了以下工作(因为您允许类似的东西):

    template<typename T, size_t N>
    void foo(array<T, N> src) { ... }
    
    ...
    
    foo('a', 'b');
    foo(1, 2, 3);
    

    方法如下:

    #include <array>
    #include <iostream>
    #include <utility>
    using namespace std;
    
    template<typename T, unsigned long N>
    void foo(array<T,N> src) { 
    
      for (auto e : src)
        cout << e << endl;
    }
    
    template<class T, class... Tail>
    auto make_array(T head, Tail... tail) -> std::array<T, 1 + sizeof...(Tail)>
    {
         std::array<T, 1 + sizeof...(Tail)> a = {{ head, tail ... }};
         return a;
    }
    
    template<class T, class... Tail> 
    void foo(T&& head, Tail&&... values) {
    
        foo(make_array(std::forward<T>(head), std::forward<Tail>(values)...));
    }
    
    int main() {
    
      foo('a', 'b');
    
      foo(1, 2, 3);
    }
    

    我已经使用 gcc 4.7.2 和 clang 3.4(主干 184647)对此进行了测试,它们按预期工作。
    这是一个在线版本at Stacked-Crooked. 但是,此代码无法在 Ideone 编译。由于我无法弄清楚传递给 Ideone 编译器的选项,因此我放弃了该网站。


    我从@Pavel MinaevHow to emulate C array initialization “int arr[] = { e1, e2, e3, … }” behaviour with std::array? 问题的回答中无耻地窃取了make_array 函数。其他 make_array 建议导致了我无法修复的编译错误。

    make_array这个函数有限制,请阅读the entire post;特别是引用了关于 comp.lang.c++.moderated 的讨论 std::array - if only it knew its size。显然,获得一个合理的make_array 是相当棘手的。 我不建议将此答案中头脑简单的 make_array 用于生产代码。


    如果大小是std::initializer_list 的模板参数,则不会有任何问题。因此问题Why is the size not a template argument of std::initializer_list?

    【讨论】:

    • 令人印象深刻...我想我需要阅读更多内容才能理解make_array... :)
    • 为什么投反对票? 答案有什么问题? OP 允许类似的东西
    【解决方案2】:

    显然不是。标准 (14.8.2.5) 将此称为非推断上下文;

    然而,在某些情况下,该值不参与类型推导,而是使用在其他地方推导或明确指定的模板参数的值。

    ...

    未推断的上下文是:

    ...

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

    例子:

    模板 void g(T);
    g({1,2,3}); // 错误:没有为 T 推导出参数

    编辑:你可以使同样的事情与std::vector一起工作,如果你只是使用initializer_list重载来使类型的推导工作;

    template<typename T>
      void foo(const std::vector<T>& src) { ...your code here... }
    template<typename T>
      void foo(const std::initializer_list<T>& src) { foo(std::vector<T>(src)); }
    
    foo({1,2,3});  // Compiles
    

    ...但遗憾的是,由于 initializer_list 的大小不是模板参数,我想不出一种方法来让它从 @987654325 中推断和转发数组 size @ 与类型相同。

    【讨论】:

    • 向量解决方案似乎可以在没有 initializer_list 重载的情况下工作。
    • @jameszhao00 在哪个 gcc 上?我目前只有 4.7.3 可以测试。编辑:啊,4.8,没看到你是问这个问题的人:)
    【解决方案3】:

    可以直接使用初始化列表来实现该语法。例如:

    #include <iostream>
    #include <initializer_list>
    
    void foo(std::initializer_list<int> il) {
      for (auto i: il)
        std::cout << i < std::endl;
    }
    
    int main() {
      foo({1,2,3});
    }
    

    或者让它更通用:

    template <typename T>
    void foo(std::initializer_list<T> il) {
      ...
    

    【讨论】:

    • 更进一步,为什么不模板foo 到任何容器?
    【解决方案4】:

    对原始数组的引用是可能的:

    template <typename T, size_t N>
    void foo(T const (&x)[N]) {
        // x is [1, 2, 3], N = 3
    }
    
    int main() {
        foo({1, 2, 3});
        return 0;
    }
    
    

    请注意,数组必须声明为const

    【讨论】:

    • 这很好。你有什么见解为什么const 允许这种隐式转换,如果有,请分享一下?
    • 是不是因为引用必须是const?
    • 我不知道。我刚刚在“C++ 模板”一书中读到它。
    • 我相信我有同样的书。关于数组和指针衰减的大量内容。你还记得那个部分吗?
    • 我认为是第 2 版的第 5.4 和 7.4 节
    猜你喜欢
    • 2016-10-06
    • 2016-04-27
    • 2019-02-27
    • 2016-07-19
    • 1970-01-01
    • 1970-01-01
    • 2021-09-21
    • 2015-01-12
    • 1970-01-01
    相关资源
    最近更新 更多