美文网首页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和不定长数组,我迷

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