C11 标准
内容目录

第一章

类型推导

auto类型推导

auto关键字新意义

auto x = 5;
auto pi = new auto(1);
const auto *y = &x, u = 6;
static auto z = 0.0;
// 错误写法:const auto *y = &x, u; 这里的u类型不确定
// 错误写法: auto s; 

在旧标准中,auto关键字用于表示具有自动存储期的局部变量,例如auto int i = 0;,但实际上,默认写成int i = 0;,因为非static变量默认都是具有自动存储期的局部变量

auto推导规则

cv限定符:表示const和volatile。
推导规则:
1、当不声明为指针或引用时,auto的推导结果和初始化表达式抛弃引用和cv限定符后类型一致。
例如:

const auto a = 1; // a -> const int
auto b = a; // b -> int

2、当声明为指针或引用时,auto的推导结果和将保持初始化表达式的cv属性。
例如:

const auto a = 1; // a -> const int
auto& b = a; // b -> const int &

注意:auto是不能用于函数参数的。

auto的限制

void add(auto a) {} // auto: 不能用户函数参数

struct Food
{
    auto price = 0; // auto不能用于非静态成员变量
};

Food<int> f;
Food<auto> d = f; // auto不能用于推导模板参数

auto用处

用于迭代器,因为类型声明太长了。

decltype关键字

获取表达式的类型

decltype(exp)

int x = 0;
decltype(x) y = 1; // y -> int

const int& i = x;
decltype(i) j = y; // j -> const int &

decltype可以像auto一样加上const限定符,且decltype可以进行精确推导,而不会舍弃掉cv限定符。

decltype的推导规则

  • 推导规则1:exp是标识符、类访问表达式,decltype(exp)和exp的类型一致。
  • 推导规则2:exp是函数调用,decltype(exp)和返回值的类型一致。
  • 推导规则3:其他情况,若exp是一个左值,则decltype(exp)是exp类型的左值引用,否则和exp的类型一致。

第一种情况:decltype作为标识符和类访问表达式的示例

class F00
{
public:
    static const int Number = 0;
    int x;
};

int n = 0;
volatile const int & x = n;

decltype(n) a = n; // a -> int
decltype(x) b = n; // b -> const volatile int &

decltype(Foo::Number) c = 0; // c -> const int

Foo foo;
decltype(foo.x) d = 0; // d -> int

第二种情况:decltype作用于函数调用


int& func_int_r(void);
int&& func_int_rr(void);
int func_int(void); // 纯右值

const int& func_cint_r(void);
const int&& func_cint_rr(void);
const int func_cint(void); // 纯右值

const Foo func_cfoo(void);

// 下面是测试语句
int x = 0;
decltype(func_int_r())  a1 = x; // int &
decltype(func_int_rr()) b1 = 0; // int &&
decltype(func_int())    c1 = 0; // int

decltype(func_cint_r())     a2 = x; // const int&
decltype(func_cint_rr())    b2 = 0; // const int&&
decltype(func_cint())       c2 = 0; // int

decltype(func_cfoo()) ff = Foo();

c2是int而不是const int, 这是因为函数返回的int是一个纯右值,对于纯右值,则只有类类型可以携带cv限定符。


第三种情况:带括号的表达式和加法运算表达式:

struct Foo { int x; };
const Foo foo = Foo();

decltype(foo.x) a = 0; // int
decltype((foo.x)) b = a; // const int&

int n = 0, m = 0;
decltype(n + m) c = 0; // c -> int
decltype(n += m) d = c; // d -> int&

decltype的实际应用

// 泛型类型定义可能存在问题的示例

template<class ContainerT>
class Foo
{
    typename ContainerT::iterator it_; 
public:
    void func(ContainerT& container)
    {
        it_ = container.begin();
    }
};

int main()
{
    typedef const std::vector<int> container_t;
    container_t arr;

    Foo<container_t> foo;
    foo.func(arr);

    return 0;
}

这会报一堆错误。因为ContainerT::iterator不能包括所有的迭代器类型,当ContainerT是一个const类型时,应当使用const_iterator

// C++98/03 下使用模板特化解决:
template<class ContainerT>
class Foo<const Container>
{
    typename ContainerT::const_iterator it_;
public:
    void func(const ContainerT& container)
    {
        it_ = container.begin();
    }
};

使用了decltype以后,就可以直接这样写:

template<class ContainerT>
class Foo
{
    decltype(ContainerT().begin()) it_;
public:
    void func(ContainerT& container)
    {
    it_ = container.begin();
    }
};

返回类型后置语法

对于该例子:

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)) add(T t, U u)
{
    return t + u;
}

// 返回类型后置语法
template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u)
{
    return t + u;
}

模板的细节改进

模板的右尖括号

// C++ 98/03不支持连续两个右尖括号的示例

//例如:

Foo<vector<int>> sb; // 编译出错
Foo<vector<int> > sb; // 正确写法加个括号

模板的别名

// 想象下面这个场景:
typedef std::map<std::string , int> map_int_t;
// ...
typedef std::map<std::string , std::string> map_int_t;

// 我们想定义一个以string为key,以其他类型为value的映射
// 在 C++ 98/03中不得不这样做:
template<typename Val>
struct str_map
{
    typedef std::map<std::string, Val> type;
};

str_map<int>::type map1;

// C11标准中的定义方式
template<typename Val>
using str_map_t = std::map<std:;string, Val>;

// 定义函数指针
typedef void (*func_t)(int, int); // C98
using func_t = void (*)(int, int); // c11

函数模板的默认模板参数

不想写了

列表初始化

初始化列表

class Foo
{
public:
    Foo(std::initializer_list<int>) {}

};

Foo foo = {1, 2, 3, 4, 5};

// 通过std::initializer_list 给自定义容器赋值示例

class FooVector
{
    std::vector<int> content_;
public:
    FooVector(std::initializer_list<int> list)
    {
        for(auto it = list.begin(); it != list.end(); ++it)
        {
            content_.push_back(*it);
        }
    }
};

防止类型收窄

基于范围的for循环

该笔记为阅读《深入应用C++11代码优化与工程级应用》祁宇(机械工业出版社),所写的笔记
上一篇
下一篇