我可以使用std::initializer_list而不是花括号来初始化数组吗?

14

我可以使用std::initializer_list对象来初始化数组而不是用大括号吗?

众所周知,我们可以这样做:http://en.cppreference.com/w/cpp/language/aggregate_initialization

unsigned char b[5]{"abc"};
// equivalent to unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'};

int ar[] = {1,2,3};
std::array<int, 3> std_ar2{ {1,2,3} };    // std::array is an aggregate
std::array<int, 3> std_ar1 = {1, 2, 3};
但我无法通过 std::initializer_list il; 初始化一个数组:

http://ideone.com/f6aflX

#include <iostream>
#include <initializer_list>
#include <array>

int main() {

    int arr1[] =  { 1, 2, 3 };  // OK
    std::array<int, 3> arr2 =  { 1, 2, 3 }; // OK

    std::initializer_list<int> il = { 1, 2, 3 };
    constexpr std::initializer_list<int> il_constexpr = { 1, 2, 3 };

    //int arr3[] = il;  // error
    //int arr4[] = il_constexpr;    // error

    //std::array<int, 3> arr5 =  il;    // error
    //std::array<int, 3> arr6 =  il_constexpr;  // error

    return 0;
}

但是我如何使用std::initializer_list il;来初始化一个数组呢?


5
重新开放了。 "duped" 是关于使用 { .. } 初始化成员数组的一般问题。这个问题要求使用 std::initializer_lists 初始化 std:: 类型(不允许对其进行任何更改!)。它们是非常不同的问题。 - Johannes Schaub - litb
@Alex 你不能使用 algorithm 中的 copy 函数吗?(示例 - skypjack
3个回答

10

其他回答正确地指出这是不可能的。但是通过一些小工具,你可以接近实现。

template<typename T, std::size_T N, std::size_t ...Ns>
std::array<T, N> make_array_impl(
    std::initializer_list<T> t,
    std::index_sequence<Ns...>) 
{
    return std::array<T, N>{ *(t.begin() + Ns) ... };
}

template<typename T, std::size_t N>
std::array<T, N> make_array(std::initializer_list<T> t) {
    if(N > t.size())
       throw std::out_of_range("that's crazy!");
    return make_array_impl<T, N>(t, std::make_index_sequence<N>());
}

如果您愿意接受更多变通方法,可以将此代码放入一个类中,以捕获静态已知长度违规情况,这适用于传递花括号初始化列表的情况。但请注意,大多数阅读此代码的人都会感到困惑。
template<typename T, std::size_t N>
struct ArrayInitializer {
    template<typename U> struct id { using type = U; };
    std::array<T, N> t;

    template<typename U = std::initializer_list<T>>
    ArrayInitializer(typename id<U>::type z) 
        :ArrayInitializer(z, std::make_index_sequence<N>())
    { 
        if(N > z.size())
            throw std::out_of_range("that's crazy!");
    }

    template<typename ...U>
    ArrayInitializer(U &&... u)
       :t{ std::forward<U>(u)... }
    { }

private:
    template<std::size_t ...Ns>
    ArrayInitializer(std::initializer_list<T>& t,
                     std::index_sequence<Ns...>)
       :t{ *(t.begin() + Ns) ... }
    { }
};

template<typename T, std::size_t N>
std::array<T, N> f(ArrayInitializer<T, N> ai) {
    return std::move(ai.t);
}

int main() {
   f<int, 5>({1, 2, 3, 4, 5});  // OK 
   f<int, 5>({1, 2, 3, 4, 5, 6});  // "too many initializers for array<int, 5>"

   std::initializer_list<int> il{1, 2, 3, 4, 5};
   f<int, 5>(il); // ok
}

请注意,答案顶部的非静态情况和“头撞桌”情况仅检查您是否提供了太少的初始化元素,如果是,则会出现错误,对于initializer_list情况。如果您为initializer_list情况提供了太多的元素,则后续元素将被忽略。

谢谢!是的,这两个例子都可以使用:http://ideone.com/s0ciUW 和 http://ideone.com/fbnM6W - Alex
我认为,使用C++17的constexpr,我们可以更优雅地处理静态已知长度错误;毕竟initializer_list.size()也是constexpr - MSalters

7
据我所知,不行:你不能使用std::initializer_list初始化std::array
问题在于,std::array旨在成为经典C风格数组的轻量级替代品(包装器)。它如此轻巧,以至于没有构造函数,只能使用隐式构造函数。
通过聚合初始化(通过隐式构造函数)进行构造是可能的,因为对于C风格数组而言是可行的。
但是std::initializer_list是一个类,比聚合初始化更复杂。
例如,您可以使用std::initializer_list初始化std::vector,但这仅因为有一个显式构造函数,用于接收std::initializer_liststd::vector。但是std::vector是一个较重的类。
我唯一看到的解决方案是两步法:(1)构建和(2)复制std::initializer_list的值。类似于以下内容:
std::array<int, 3> arr5;

auto  ui = 0U;
auto  cit = il.cbegin();

while ( (ui < arr5.size()) && (cit != il.cend()) )
   arr5[ui++] = *cit++;

p.s.:抱歉我的英语不好。


8
英语不好?你的写作比我的大多数同事都要好。在 Stack Overflow 的答案或问题中,不需要这样的评论。如果有任何不易理解的内容,社区会进行编辑。 - Andon M. Coleman
2
@AndonM.Coleman - 谢谢;我会尽量忽略我对语言的不安全感。 - max66

3

哪个POD的具体要求禁止这样做? - chris
@chris:抱歉,不是 POD,而是 Aggregate 类型。请参阅 http://en.cppreference.com/w/cpp/language/aggregate_initialization 和 http://en.cppreference.com/w/cpp/container/array。我刚刚修正了文本。 - Emilio Garavaglia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接