编程语言
首页 > 编程语言> > decltype,initializer_list,variadic templates学习记录(c++)

decltype,initializer_list,variadic templates学习记录(c++)

作者:互联网

decltype,initializer_list,variadic templates

decltype

decltype的作用就是的到一个表达式或者变量的类型

#include <iostream>
using namespace std;

int getSize()
{
    cout << "call getSize()" << endl;
}

int main()
{
    int tempA = 2;
    /*1.dclTempA为int.*/
    decltype(tempA) dclTempA;
    /*2.dclTempB为int,对于getSize根本没有定义,但是程序依旧正常,因为decltype只做分析,并不调用getSize().*/
    decltype(getSize()) dclTempB;

    return 0;
}

在这里插入图片描述
并没有调用getSize,只是分析decltype的类型

#include <iostream>
using namespace std;
int main()
{
    int tempA = 0, &refTempA = tempA;
    /*1.dclTempA为引用,绑定到tempA*/
    decltype(refTempA) dclTempA = tempA;
    /*2.dclTempB为引用,必须绑定到变量,编译不过*/
    decltype(refTempA) dclTempB = 0;
    /*3.dclTempC为引用,必须初始化,编译不过*/
    decltype(refTempA) dclTempC;
    /*4.双层括号表示引用,dclTempD为引用,绑定到tempA*/
    decltype((tempA)) dclTempD = tempA;
    
    const int ctempA = 1, &crefTempA = ctempA;
    
    /*5.dclTempE为常量引用,可以绑定到普通变量tempA*/
    decltype(crefTempA) dclTempE = tempA;
    /*6.dclTempF为常量引用,可以绑定到常量ctempA*/
    decltype(crefTempA) dclTempF = ctempA;
    /*7.dclTempG为常量引用,绑定到一个临时变量*/
    decltype(crefTempA) dclTempG = 0;
    /*8.dclTempH为常量引用,必须初始化,编译不过*/
    decltype(crefTempA) dclTempH;
    /*9.双层括号表示引用,dclTempI为常量引用,可以绑定到普通变量tempA*/
    decltype((ctempA))  dclTempI = ctempA;
}
#include <iostream>
using namespace std;
int main()
{
    int tempA = 2;
    int *ptrTempA = &tempA;
    /*1.常规使用dclTempA为一个int *的指针*/
    decltype(ptrTempA) dclTempA;
    /*2.需要特别注意,表达式内容为解引用操作,dclTempB为一个引用,引用必须初始化,故编译不过*/
    decltype(*ptrTempA) dclTempB;
}

在这里插入图片描述
上面图片上的用法应该是一个比较适用的用法了,我们不知道lambda的类型,在容器的声明的时候就可以用decltype,当然这个也可以用function来包裹。

参考博客:https://www.cnblogs.com/ghbjimmy/p/10636030.html

lnitializer_list

C++11提供的新类型,定义在<initializer_list>头文件中。

template< class T >
class initializer_list;
先说它的用处吧,然后再详细介绍一下。

首先有了initializer_list之后,对于STL的container的初始化就方便多了,比如以前初始化一个vector需要这样:

int a[] = {0, 1, 2, 3};
std::vector<int> vec(a, a+sizeof(a));

或者

std::vector<int> vec;
vec.push_back(1);
vec.push_back(3);
vec.push_back(3);
vec.push_back(2);

有了initializer_list后,就可以直接像初始化数组一样:

class Test {
private:
    static std::map<string, string> const nameToBirthday = {
        {"lisi", "18841011"},
        {"zhangsan", "18850123"},
        {"wangwu", "18870908"},
        {"zhaoliu", "18810316"},
    };
}

当然啦,里面的std::map必须提供参数为initializer_list的构造函数如:

map( std::initializer_list<value_type> init,
const Compare& comp = Compare(),
const Allocator& alloc = Allocator() );
其实for(initializer: list)中如果list是个形如:{a, b, c…},那么其实list自动被构造成了initializer_list对象。

下面稍微介绍一下initializer_list
一个initializer_list当出现在以下两种情况的被自动构造:
1.当初始化的时候使用的是大括号初始化,被自动构造。包括函数调用时和赋值
2.当涉及到for(initializer: list),list被自动构造成initializer_list对象也就是说initializer_list对象只能用大括号{}初始化。

拷贝一个initializer_list对象并不会拷贝里面的元素。其实只是引用而已。而且里面的元素全部都是const的。

下面一个例子可以帮助我们更好的理解如何使用initializer_list:

#include <iostream>
#include <vector>
#include <initializer_list>

using namespace std;

template <class T>
struct S {
    vector<T> v;
    S(initializer_list<T> l) : v(l){
        cout << "constructed with a " << l.size() << "-elements lists" << endl;
    }
    void append(std::initializer_list<T> l) {
        v.insert(v.end(), l.begin(), l.end());
    }

    pair<const T*, size_t> c_arr() const{
        return {&v[0], v.size()};
    }

};


template <typename T>
void templated_fn(T arg) {
    for (auto a : arg)
        cout << a << " ";
    cout << endl;
}

int main() {
    S<int> s = {1, 2, 3, 4, 5}; //automatically construct a initializer_list 
                                // object and copy it
    s.append({6, 7 , 8});         //list-initialization in function call

    cout << "The vector size is now " << s.c_arr().second << " ints:" << endl;

    for (auto n : s.v)
        cout << ' ' << n;
    cout << endl;

    cout << "range-for over brace-init-list: " << endl;
    
    for (auto x : {-1, -2, 03})    the rule for auto makes this ranged for work
        cout << x << " ";
    cout << endl;

    auto al = {10, 11, 12};  //special rule for auto

    cout << "The list bound to auto has size() = " << al.size() << endl;


    //templated_fn({1, 2, 3});   //compiler error! "{1, 2, 3}" is not an expressionit has no type, and so T cannot be duduced.

    templated_fn<initializer_list<int> > ({7, 8, 9}); //ok
    templated_fn<vector<int> >({3, 5, 7});           //also ok

    return 0;
}

另外,initializer_list底部值array容器在做支撑。

Variadic Templates

在这里插入图片描述
上图是使用变参模板来实现printf函数可以传入任意参数,并将参数打印出来,就是通过不断地减少参数,递归的调用printf函数知道后面的参数没有了就输出后面的字符串,上面一个例子就是\n。

另外一个就是找出最大值:
在这里插入图片描述在这里插入图片描述
如果是类型相同的比较大小,上面的方法就能满足,代码也容易懂。

在这里插入图片描述
变参模板的实现:
这样就可以传入任意类型的参数,至于不同类型的参数能不能比大小就看你有没有关于这两种操作符的重载函数了。

这个列子就是如果这第一个参数和最后一个参数有一些特殊的处理,可以看看下一个例子:

在这里插入图片描述
其实可以看见,上面的做法就是在最后的时候进行一个偏特化,这样当tuple只剩最后一个参数时,就会调用到这个函数。

在这里插入图片描述
在这里插入图片描述
第一张构造函数那里的返回类型编译会报错,下面的写法是对的

这个就是tuple的实现,tuple是元组,类似于数据库中一张表中的一条记录。
重点就是在继承:private tuple,这样做就会产生递归的继承,就是图片右边所示,下面就是内存的结构图,这样递归的继承就会把tuple所需要的数据成员构造出来。

在这里插入图片描述
上面的是通过递归的复合来实现tuple的功能,composited m_tail就会递归的复合下去。

参考资料:侯捷老师的视频

标签:templates,std,tempA,decltype,int,list,initializer
来源: https://blog.csdn.net/qq_43812167/article/details/113824621