C++ 11 14 17

Created by miccall (转载请注明出处)

一.类型选择

1.auto关键字

auto 关键字声明变量代替了类型 编译器在编译时,会自动替换为推导出来的类型。
推导规则一般是初始化赋值的返回值类型。这个应用场景就可以避免过多的已知类型

比如 迭代器的声明 ,明明可以从 begin() 的返回值判断类型 还有重复声明 iter的类型 显得繁杂。用auto代替。


    std::map<int,int> m_map;
    // map<int,int>::iterator iter =  m_map.begin();
    auto iter = multimap1.begin();

如果觉得不过瘾,还可以看看


    std::pair<
            std::unordered_multimap<int,int>::iterator,
            std::unordered_multimap<int,int>::iterator
            > 
         range = multimap1.equal_range(1);

    auto range2 = multimap1.equal_range(2);

auto有时候会简化我们的书写。

因为是编译过程中,编译器为我们替换,而免去了我们自己写,所以用auto声明的变量必须初始化 。

说到这里,突然想到参数传递,又想到函数模板,恰恰他们俩的作用是相似的。而我试过,auto不能用于函数的参数类型,还是得写模板。

还有auto不能的为 不能声明非静态成员,数组,还有范型。

    struct{
        auto v = 0 ; //错误
        static const auto v2 = 0 ; 
    }

    auto arr[10] = {0}; //错误 

    std::map<auto,auto> m_map; //错误 

auto还可以用于我们不知道定义类型的时候 ,比如使用函数模板,传入的typeanme可以是任意类型的,我们就可以使用auto自动接入。


template<class A>
void func()
{
    auto var = A::get();
}

2.decltype() 获取类型

auto是编译器推导类型,那么decltype()可以看作是编译器计算类型。
比如说 int a = 1+1 。那么a的类型就可以表示为 decltype(1+1) ,此时 它与int就是等价的。
这样做就可以解决auto声明变量必须初始化的问题。

    int x = 1 ;
    decltype(x) y ;// y的类型也是int

看到 decltype(1+1) 就会感觉 decltype()的参数应该不简单,是的没错,他的参数是一个表达式,用来计算类型。
这里讲一下 纯右值 , 在此之前看一下左值和右值,通常来说有名字的变量就是左值 , 有名字的常量就是右值,而由运算操作(加减乘除,函数调用返回值等)所产生的中间结果(没有名字)就是纯右值。

    // 左值 a ,b, p
    int a ;
    int& b = 1 ; 
    int* p = &a ;

    // 右值
    const a ;  
    2 ;

    //纯右值
    int &a = b ;

他的实际用处呢 ,就在于包含的对象的计算


template<class T>
void func()
{
    typename T::iterator t1 ;
}

这样貌似没什么问题 ,但是实际使用起来,难免会有问题 ,因为T::iterator不包含所有的迭代器类型,还包括const类型的。
所以我们改一下


    template<class T>
    void func()
    {
        decltype(T::begin()) t1 ;
    }

通过T::begin() 方法返回的类型,就可以让编译器推出他自己的迭代器类型了。

3.auto 和 decltype() 的混合使用

使用场景 我们范型编程,需要返回值为范型的推导,我们就得使用一个巧妙的设计方式了


    template <typename T,typename U,typename R>
    R add(T t,U u)
    {
        return t+u;
    };

这个怎么使用呢?emmm 写 auto 吗


    auto a = add<decltype(x+y)>(x,y);

仿佛也可以,但是问题是,外界调用怎么知道范型的类型是x+y呢? 不行,这个还得交给函数做。

    template <typename T,typename U>
    decltype( t+u ) add(T t,U u)
    {
        return t+u;
    };

但是这样的写法是错误的 ,t和u使用之前必须定义。

    template <typename T,typename U>
    decltype(T()+U()) add(T t,U u)
    {
        return t+u;
    };

这样解决了函数外部范型问题,但是T()和U() ,都需要一个构造函数,没有怎么办?我们还得继续改

    template <typename T,typename U>
    decltype( (*(T*)0) + (*(U*)0) ) add2(T t,U u)
    {
        // T* 是指针 ,用0表示
        // *0 表示的是0所指向的原数据
        return t+u ;
    };

这样大概就清晰了 ,但是复杂的写法也让阅读成为障碍 。c++ 11 引入了新的语法,返回值后置写法


    template <typename T,typename U>
    auto add3(T t,U u) -> decltype( t+u)
    {
        return t+u ;
    };

这样的写法,完美的解决了我们的问题 。

    int& foo(int& a);
    float foo(float& b);

    template <typename T>
    auto func(T& val) -> decltype(foo(val))
    {
        return foo(val);
    }

实际使用中 ,我们可以通过这样的书写,来避开各种类型的困扰。