美文网首页C++ 2a
关于std::initializer_list和不定长数组,我迷

关于std::initializer_list和不定长数组,我迷

作者: 左图右码 | 来源:发表于2022-06-20 12:12 被阅读0次

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);

原创不易,拒绝转载。

相关文章

  • 关于std::initializer_list和不定长数组,我迷

    std::initializer_list的设计很简单,但在classical C++中充当了越来越重要的角色,是...

  • C++11:std::initializer_list

    基本概念 std::initializer_list 类型对象是一个访问 const T 类型对象数组的轻量...

  • c++11强化知识点

    初始化列表(std::initializer_list) c++11提供了std::initializer_lis...

  • oracle 数组学习

    oracle数组可以分为定长数组和可变数组。 一、定长数组:create or replace procedur...

  • Scala基础——数组

    定长数组 数组一般包括定长数组和变长数组,在Scala中使用Array进行声明定长数组注意:scalad的索引标示...

  • golang入门到放弃:3.数组、切片、map

    数组 切片 数组和切片的区别 数组必须要指定长度,如果定义时不指定长度,go语言也会根据元素中的个数自动设置值数组...

  • 库目录

    数组和切片 数组是固定长度的,保存的是值类型 切片是不固定长度的,保存的是底层数组的引用 切片可以添加元素,容量不...

  • scala数组

    scala中的数组分为定长数组(Array)和可变长数组(ArrayBuffer) 定长数组(Array):1.声...

  • c++ 11 特性

    lambda特性 括号初始化列表 std::initializer_list 代理构造函数 字符串字面增量 用户定...

  • vector数组知识汇总

    1.关于vector数组 我个人觉得vector是个不固定长的数组,可以广义上认为是数组的增强版,所以能有各种对自...

网友评论

    本文标题:关于std::initializer_list和不定长数组,我迷

    本文链接:https://www.haomeiwen.com/subject/fyxjvrtx.html