关于std::initializer_list和不定长数组,我迷
std::initializer_list的设计很简单,但在classical C++中充当了越来越重要的角色,是标准的一定公民,在编译器级别收到支持。
严格说,native C++不支持不定长参数(在C++/CLI中有个例外),native C++一般通过重载来模拟不定长参数。#include <stdarg.h>中的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<int> vector(1,2,3,4,5); //error
但可以让不定长参数转化为std::initializer_list,比如,我有个自定义的stack类,有如下的构造,调用了placement new:
stack(std::initializer_list<T> const& list)
{
if (list.size() > 0)
pushArray(&*std::begin(list), list.size());
}
定义一个不定长参数的构造,可以轻易地进行转化:
template<typename... Args>
stack(Args... args)
{
::new(this)stack({ std::forward<Args>(args)... });
}
将std::forward<Args>(args)... 放入初始化列表里,可以将其展开,如果不考虑完美转发,可以这么写:
::new(this)stack({args... });//调用通过初始化列表的构造函数
你想到了委托构造了吗?可以直接委托成其它的构造函数来实现(C++11 new feature)
template<typename... Args>
stack(Args... args) : stack({ std::forward<Args>(args)... })
{ }
所以,你有了这个构造,就不用多输入一个花括号了:
stack <int> s(1,2,3,4,5); // is ok
标准的不定长数组的展开是通过特化与递归,在此,类型可以是各异的,如下:
template<typename T>
void print(const T& val)
{
std::cout << val << std::endl;
}
template<typename T,typename... Args>
void print(const T& val,Args... args)
{
print(val);
print(std::forward<Args>(args)...);
}
如果可以就地展开,则不需要递归了,下面使用了逗号运算符号,就能就地展开:
template<typename T>
void printValue(const T& val)
{
std::cout << val << std::endl;
}
template<typename T,typename... Args>
void print(Args... args)
{
auto arr = {(printValue(args),0)...};
//st::std::initializer_list<int>{(print(args),0)...}; // is ok
}
逗号运算符是个稍冷僻的概念,如(a,b,c,d,e)的计算结果是最后一个的值:e,但a、b、c、d、e会依次执行。
native c++通过模版使用函数重载模拟了接受多参的函数,而C++/CLI就纯粹多了,不用模版,直接可以声明多参函数,如:
void sum(... cli::array<int>^ args)
{
int ret = 0;
for each(int c in args)
{
ret += c;
}
return ret;
}
...
int val = sum(1,2,3,4,5);
原创不易,拒绝转载。