C++11 std::initializer_list

如何使用

std::initializer_list是 C++11 新增加的轻量级类模板,主要用于配合 { } 初始化列表传递参数使用。下面的例子中,作为构造函数参数,可以实现不定数量的参数

    struct SSizeB {
        int width;
        int height;

    public:
        SSizeB(std::initializer_list<int> valueList) : width(0), height(0)
        {
            if (valueList.size() >= 1)
            {
                this->width = valueList.begin()[0];
            }

            if (valueList.size() >= 2)            {
                this->height = valueList.begin()[1];
            }
        }
    };
    SSizeB sizeb_1 = {11, 199992};
    SSizeB sizeb_2 = {11};

需要支出的是,std::initializer_list 是一个轻量级的容器,可以接收任意长度的初始化列表,只能被整体赋值或者初始化,只提供了3个成员接口:size(), begin(), end()
std::initializer_list 甚至没有提供 operator[] ,所以如果我们想访问某个下标的对象,只能通过类似于 valueList.begin()[1]; 这样使用迭代器来实现,而不能够使用 valueList[1] 这种便捷方式。需要提醒的是,一定要注意迭代器是否越界的处理。

性能

std::initializer_list 在初始化时仍然要执行拷贝构造的工作,所以,auto valueList = {a}; 会调用拷贝构造函数。和 std::vector 等容器一样,std::initializer_list 是不能直接存储对象的引用的,比如 std::initializer_list<int&>,所以,有些时候需要考虑到拷贝构造带来的性能开销。

    class A
    {
    public:
        int x;

        A(int _x) :x(_x) {
            std::cout << "A()" << std::endl;
        }

        A(A const & a) :x(a.x)
        {
            std::cout << "A(A const& a)" << std::endl;
        }
    };


    A a(1); // call : A()
    auto valueList = {a};// call : A(A const & a)

虽然 STL 容器无法直接存储引用类型,但是可以使用 std::reference_wrapper 来存储引用,如下:

A a(1); // call : A()
std::initializer_list<std::reference_wrapper<A>> refList = {a};

这样 refList 初始化时就不会调用 A 的拷贝构造函数了,性能会得到提升。

实现不定参数的函数

std::initializer_list 的一个使用场景是实现不定参数的函数

void log(std::initializer_list<std::string> params)
{
    for (auto s : params)
    {
        std::cout << s;
    }
}

log({ "A", "B", "C" });
log({ "D" });