C++ 11/14/17 新特性

c++新特性

nullptr

空指针 防止隐式的类型转换 NULL 和 0

constexpr

常量表达式 constexpr函数可以使用递归

constexpr int fibonacci(const int n) {
    return n == 1 || n == 2 ? 1 : fibonacci(n-1)+fibonacci(n-2);
}

并且从c++14开始,constexpr函数内可以使用局部变量,循环,分支等简单语句

if/switch 语句初始化

c++17让我们可以在if或switch时对变量进行初始化

//将临时变量放到if语句中
if (初始化临时变量; 逻辑判断表达式) {
    代码块
}

初始化列表

c++11将初始化列表的概念绑定到了类型上,并称之为std::initializer_list,他允许构造函数或其他函数像参数一样使用初始化列表

std::vector<int> vec;
MagicFoo(std::initializer_list<int> list) {
    for (std::initializer_list<int>::iterator it = list.begin();it != list.end(); ++it)
    vec.push_back(*it);
}
MagicFoo magicFoo = {1, 2, 3, 4, 5};

除了用于构造对象,还可以作为普通函数的形参

结构化绑定

c++11让我们可以用tuple构造元组,但是没有简单的方法可以直接拿到并定义元组中的元素,可以使用tie对元组进行拆包,但是需要清楚元组包含多少对象,类型等

c++17让我们可以这样做

std::tuple<int, double, std::string> f() {
    return std::make_tuple(1, 2.3, "456");
}
auto [x, y, z] = f();

类型推导

auto可以让编译器进行类型推导

decltype是为了解决auto关键字只能对变量进行类型推导的缺陷而出现的

用法是decltype(表达式)

is_same<T, U>可以用来判断T和U这两个类型是否相等

由于auto不能推导函数返回值类型,所以c++11引入了尾返回类型

template<typename T, typename U>
auto add(T x, U y) -> decltype(x+y) {
    return x + y;
}

从c++14开始可以直接让普通函数具备返回值推导,所以就可以使用auto对函数返回值进行推导

decltype(auto) 主要用于对转发函数或封装的返回类型进行推导,使我们无需显式的指定decltype的参数表达式

if constexpr

c++17将constexpr关键字引入到if语句中,允许代码中声明常量表达式的判断条件

template<typename T>
auto print_type_info(const T& t) {
    if constexpr (std::is_integral<T>::value) {
        return t + 1;
    } else {
        return t + 0.001;
    }
}

区间for迭代

for (auto &x : vec) {
    //statement
}

加上引用就是可写的,不加就是只读的

模板

传统C++中,只要编译过程中遇到了被完整定义的模板,都会实例化,这就导致了重复实例化导致的编译时间的增加。

C++11引入外部模板,让我们可以通知编译器何时进行模板的实例化

template class std::vector<bool>; // 强行实例化
extern template class std::vector<double>; // 不在该当前编译文件中实例化模板

类型别名模板,模板是用来产生类型的,传统C++中,typedef可以为类型定义一个新的名称,但是无法为模板定义,因为他不是类型

使用using可以达成功效,同时using也有和传统的typedef相同的效果

typedef int (*process)(void *);
using NewProcess = int(*)(void *);
template<typename T>
using TrueDarkMagic = MagicType<std::vector<T>, std::string>;

int main() {
    TrueDarkMagic<bool> you;
}

既然我们有默认的函数参数,那么也可以有默认的模板参数

template<typename T = int, typename U = int>
auto add(T x, U y) -> decltype(x+y) {
    return x+y;
}

这样不需要每次指定add的类型也可以使用

变长参数模板,结合python等语言中的不定长参数,并且类型是可以变化的

template<typename... Ts> class Magic;

个数为0的模板参数也是被允许的class Magic<> nothing;

所以可以手动定义至少一个模板参数来避免这种情况

template<typename Required, typename... Ts> class Magic;

可以使用sizeof...(Ts)来获得参数个数

对于解包参数来说,可以使用递归解包的方法

//很巧妙的利用了重载
template<typename T0>
void printf1(T0 value) {
    std::cout << value << std::endl;
}
template<typename T, typename... Ts>
void printf1(T value, Ts... args) {
    std::cout << value << std::endl;
    printf1(args...);
}
int main() {
    printf1(1, 2, "123", 1.1);
    return 0;
}

还可以使用变参模板展开的方法,C++17中增加了变参模板展开的支持,我们可以利用if constexpr在编译时展开的特性,对变参模板进行处理

template<typename T0, typename... T>
void printf2(T0 t0, T... t) {
    //代码会在编译时完成分支的判断并展开函数
    std::cout << t0 << std::endl;
    if constexpr (sizeof...(t) > 0) printf2(t...);
}

初始化列表展开的形式,恕我暂时还不能理解

template<typename T, typename... Ts>
auto printf1(T value, Ts... args) {
    std::cout << value << std::endl;
    (void) std::initializer_list<T> {([&args] {
        std::cout << args << std::endl;
    }(), value)...};
}
//在{}中的参数作为初始化列表,(args)...被展开为(args1), (args2), ...
//利用逗号表达式,按顺序执行并返回最后一个的结果
//所以这里是通过逗号表达式先调用了lambda函数,再把value赋给初始化列表

对于变参模板展开的方式可以看看这个文章

C++17将变长参数的特性带给了表达式

template<typename ... T>
auto sum(T ... t) {
    return (t + ...);
}
int main() {
    std::cout << sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << std::endl;
}

非类型模板参数,我们可以使用字面量作为模板的参数进行传递,并且C++17允许我们使用auto来让编译器辅助进行类型推导

template <typename T, int BufSize>
class buffer_t {
public:
    T& alloc();
    void free(T& item);
private:
    T data[BufSize];
}
buffer_t<int, 100> buf; // 100 作为模板参数

template <auto value> void foo() {
    std::cout << value << std::endl;
    return;
}
//编译器将会推导传入的类型

面向对象

C++11引入了委托构造的概念,可以在同一个类中一个构造函数调用另一个构造函数,和JAVA的类似,比如有参构造函数取调用无参构造函数

继承构造,使用using进行继承构造,即在子类直接将父类的构造函数继承过来,有待深究

class Subclass :public Base {
public:
    using Base::Base;   //继承构造
};

考虑这样两种情况,子类意外的重写了父类的虚函数,或当父类的虚函数删除后,子类的函数变成了普通的函数,为了防止这种情况,C++11引入了override和final这两个关键字

重写虚函数时,引入override关键字是显式的告诉编译器进行重写,编译器将检查是否存在这样的虚函数,否则将无法通过编译

这里要注意override(重写)和overload(重载)的区别

struct Base {
    virtual void foo(int);
};
struct SubClass: Base {
    virtual void foo(int) override; //合法
    virtual void foo(float) override; //非法,因为没有找到这个虚函数
};

final关键字是防止类被继续继承以及防止虚函数被继续重写

struct SubClass1 final: Base {};    //子类不能再继承SubClass1类
virtual void foo() final;   //子类不能再重写虚函数

显式的声明和禁用默认函数,在effective c++中,有提到如果不希望类被拷贝,就要把拷贝构造函数和赋值运算符声明为private。

并且,编译器产生的默认构造函数和用户定义的构造函数无法同时存在,若用户定义了任何构造函数,编译器将不再生成默认构造函数。

class Magic {
public:
    Magic() = default;  // 显式声明使用编译器生成的默认构造函数
    Magic& operator=(const Magic&) = delete; // 显式拒绝编译器生成构造函数
    Magic(int magic_number);
}

传统C++中,枚举类型会被视为整数,这会让两种完全不同的枚举类型可以进行直接的比较,甚至同一个命名空间中的不同枚举类型的枚举值名字不能相同

enum class new_enum :unsigned int {
    value1,
    value2,
    value3 = 100,
    value4 = 100
};

C++11引入了枚举类,他不会被隐式的转换为整数,如果相同的枚举值之间指定的值相同,是可以进行比较的,枚举类型后面使用了冒号加关键字来指定枚举值的类型,默认为int

可以重载 << 来输出枚举值

//有待研究.jpg
template<typename T>
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e) {
    return stream << static_cast<typename std::underlying_type<T>::type>(e);
}

std::enable_if和SFINAE

lambda表达式

[捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 {
    // 函数体
}

lambda表达式内部函数体默认情况下是不能够使用函数体外部的变量的,这时捕获列表可以起到传递外部数据的作用

  1. 值捕获 值捕获的前提是变量可以拷贝,被捕获的变量在lambda表达式被创建时拷贝,而不是调用时再拷贝

  2. 引用捕获 与引用传参类似,值会发生变化

  3. 隐式捕获 手动写捕获列表较为复杂,可以在捕获列表中写一个 & 或者 = 来向编译器声明采用引用捕获还是值捕获

  4. 表达式捕获 上面的捕获都是已经在外层作用域声明的变量,因为这些捕获方式均为左值捕获,C++14允许捕获的成员用任意的表达式进行初始化,这就允许了右值捕获

泛型lambda:auto关键字不能写在参数表里是因为会与模板产生冲突,C++14开始,lambda函数的参数可以使用auto来产生意义上的泛型

auto add = [](auto x, auto y) {
    return x + y;
};
add(1, 2);
add(1.1, 2.2);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值