关于std::initializer_list和不定长数组,我迷惑够久了

std::initializer_list的设计很简单,但在classical C++中充当了越来越重要的角色,是标准的一定公民,在编译器级别收到支持。
严格说,native C++不支持不定长参数(在C++/CLI中有个例外),native C++一般通过重载来模拟不定长参数。#include 中的va_arg属于C的遗产,在/clr编译时候的警告会说明使用va_arg系列函数的代码会编译成native代码才能符合改函数所要求的压栈方式。
但通过std::initializer_list让cpp间接支持了不定长参数,但类型要同构的。
很多class都支持std::initializer_list参数的构造,但不支持不定长参数的构造,比如std::vector和std::list。

std::vector vec = {1,2,3,4,5};
std::list lst = {1,2,3,4,5};

但不能这么写:

std::vector vector(1,2,3,4,5); //error

但可以让不定长参数转化为std::initializer_list,比如,我有个自定义的stack类,有如下的构造,调用了placement new:

stack(std::initializer_list const& list)
{
    if (list.size() > 0)
        pushArray(&*std::begin(list), list.size()); 
}

定义一个不定长参数的构造,可以轻易地进行转化:

template
stack(Args... args)
{
    ::new(this)stack({ std::forward(args)... });
}

将std::forward(args)... 放入初始化列表里,可以将其展开,如果不考虑完美转发,可以这么写:

::new(this)stack({args... });//调用通过初始化列表的构造函数

你想到了委托构造了吗?可以直接委托成其它的构造函数来实现(C++11 new feature)

template
stack(Args... args) : stack({ std::forward(args)... })
{ }

所以,你有了这个构造,就不用多输入一个花括号了:

stack  s(1,2,3,4,5); // is ok

标准的不定长数组的展开是通过特化与递归,在此,类型可以是各异的,如下:

template
void print(const T& val)
{
    std::cout << val << std::endl;
}
template
void print(const T& val,Args... args)
{
    print(val);
    print(std::forward(args)...);
}

如果可以就地展开,则不需要递归了,下面使用了逗号运算符号,就能就地展开:

template
void printValue(const T& val)
{
    std::cout << val << std::endl;
}
template
void print(Args... args)
{
    auto arr = {(printValue(args),0)...};
    //st::std::initializer_list{(print(args),0)...};  // is ok
}

逗号运算符是个稍冷僻的概念,如(a,b,c,d,e)的计算结果是最后一个的值:e,但a、b、c、d、e会依次执行。

native c++通过模版使用函数重载模拟了接受多参的函数,而C++/CLI就纯粹多了,不用模版,直接可以声明多参函数,如:

void sum(... cli::array^ args)
{
 int ret = 0;
 for each(int c in args)
 {
    ret += c;
 }
 return ret;
}
...
int val = sum(1,2,3,4,5);

原创不易,拒绝转载。

你可能感兴趣的:(关于std::initializer_list和不定长数组,我迷惑够久了)