More than code

More Than Code
The efficiency of your iteration of reading, practicing and thinking decides your understanding of the world.
  1. 首页
  2. C++
  3. 正文

C++ 11/14/17 新特性

2020年12月13日 110046点热度 2人点赞 1条评论

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);
标签: 暂无
最后更新:2020年12月13日

sheep

think again

点赞
下一篇 >

文章评论

  • купить мебель в фабрике мебели

    I have been surfing online more than 3 hours today, yet I never found any interesting article like yours.
    It is pretty worth enough for me. Personally, if all site owners and bloggers made good content as you did, the net will be a lot more useful than ever
    before.

    2021年4月15日
    回复
  • 取消回复

    COPYRIGHT © 2021 heavensheep.xyz. ALL RIGHTS RESERVED.

    THEME KRATOS MADE BY VTROIS