首页 > 其他分享 >cpp14关键新增特性理解

cpp14关键新增特性理解

时间:2023-07-20 14:33:15浏览次数:48  
标签:编译 int 代码 编程 特性 cpp14 关键 类型 模板

new/delete elision

"new/delete elision" 是 C++ 中的一个优化技术,用于减少由于动态内存分配和释放而产生的性能开销。它发生在编译器优化的过程中,可以将某些动态内存分配和释放的操作消除,从而提高程序的执行效率。

具体来说,"new/delete elision" 是指在一些情况下,编译器会自动优化代码,将使用 "new" 运算符分配内存的对象直接放在栈上,而不是在堆上进行动态内存分配,从而避免了动态内存分配和释放的开销。

这种优化通常在以下情况下发生:

  1. 对于小型对象:如果对象相对较小,那么将其放在栈上可能比在堆上分配更高效。编译器可以根据对象的大小和复杂性来判断是否进行 "new/delete elision"。
  2. 短期生存周期:如果对象的生存周期很短,只在某个函数或代码块中使用,那么将其放在栈上可以避免频繁的堆内存分配和释放。

需要注意的是,"new/delete elision" 是由编译器进行的自动优化,开发者不需要显式地指定。优化是否发生取决于编译器的具体实现和优化级别。

举个简单的例子:

#include <iostream>

class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructor" << std::endl;
    }

    ~MyClass() {
        std::cout << "MyClass destructor" << std::endl;
    }
};

int main() {
    MyClass obj; // 使用 "new" 关键字动态分配内存
    return 0;
}

在这个例子中,对象 obj 使用 "new" 运算符动态分配内存,因此会在堆上分配内存空间并调用构造函数。然而,由于 obj 是一个局部对象且没有指定为指针,编译器可能会进行 "new/delete elision" 优化,将 obj 直接放在栈上分配内存,从而避免了堆内存分配和释放的开销。

统一初始化

统一初始化是 C++11 中引入的一个特性,它允许我们使用统一的语法来初始化各种类型的对象,包括内置类型、自定义类类型、数组和 STL 容器等。

在 C++11 之前,对象的初始化方式较为繁琐,不同类型的对象使用不同的初始化语法,例如:

  1. 内置类型:
int x = 42;
double y = 3.14;
  1. 自定义类类型:
class MyClass {
public:
    MyClass(int a, double b) : x(a), y(b) {}
private:
    int x;
    double y;
};

MyClass obj(10, 2.5);
  1. 数组:
int arr[3] = {1, 2, 3};

C++11 引入了统一初始化初始化语法,使用花括号 {} 来进行初始化,无论是内置类型、自定义类类型、数组还是容器,都可以使用这种语法来初始化:

  1. 内置类型:
int x{42};
double y{3.14};
  1. 自定义类类型:
class MyClass {
public:
    MyClass(int a, double b) : x(a), y(b) {}
private:
    int x;
    double y;
};

MyClass obj{10, 2.5};
  1. 数组:
int arr[]{1, 2, 3};
  1. 容器:
std::vector<int> vec{1, 2, 3};
std::map<std::string, int> myMap{{"one", 1}, {"two", 2}};

使用统一初始化语法有几个好处:

  • 可以用一种统一的语法来初始化不同类型的对象,使代码更加一致和易读。
  • 可以避免一些窄化转换(narrowing conversions)问题,统一初始化在编译时进行类型检查,如果初始化值不适合目标类型,则会产生编译错误。
  • 可以更方便地使用初始化列表进行初始化,尤其对于自定义类类型和容器类型,初始化列表可以简化代码。

需要注意的是,统一初始化的语法使用花括号 {},而不是传统的圆括号 (),因此请确保在初始化时使用正确的括号。同时,在某些情况下,统一初始化可能导致歧义或产生不符合预期的结果,因此在使用时应当谨慎,并遵循最佳实践。

SFINAE(非新特性)

补充整理、理解

SFINAE 是 C++ 中的一个编程术语,它代表"Substitution Failure Is Not An Error",即"替换失败并非错误"。SFINAE 是一种编译时的模板元编程技术,用于在模板实例化时根据类型匹配进行选择和排除候选函数或模板特化。

当进行函数模板实例化时,编译器会根据实参的类型进行类型推导,并尝试替换函数模板的模板参数。如果实例化过程中发生错误(例如无法匹配函数调用或无法推导出合适的类型),传统的 C++ 编译器通常会抛出错误。然而,SFINAE 技术通过在模板实例化中允许部分替换失败,避免将这样的错误视为编译错误,而是选择继续查找其他可行的候选函数或特化。

SFINAE 的主要应用场景是在模板元编程中,特别是在实现泛型代码时,根据类型特性对函数模板进行选择或排除。

一种常见的 SFINAE 使用场景是通过使用模板函数的返回类型和参数列表中的 decltype 表达式来实现条件选择。如果 decltype 表达式能够成功推导出类型,则函数模板可行,否则编译器将忽略该模板,而不会报错。

以下是一个简单的示例,展示了如何使用 SFINAE 来实现条件选择:

#include <iostream>
#include <type_traits>

// SFINAE:如果 T 类型有成员函数 print(),则调用 print();否则什么都不做
template <typename T>
typename std::enable_if_t<std::is_same_v<decltype(std::declval<T>().print()), void>>
doPrint(const T& obj) {
    obj.print();
}

// SFINAE:如果 T 类型没有成员函数 print(),则调用该函数,输出默认消息
template <typename T>
typename std::enable_if_t<!std::is_same_v<decltype(std::declval<T>().print()), void>>
doPrint(const T& obj) {
    std::cout << "Default print message" << std::endl;
}

// 测试类
class HasPrint {
public:
    void print() const {
        std::cout << "HasPrint::print()" << std::endl;
    }
};

class NoPrint {
    // 没有 print() 成员函数
};

int main() {
    HasPrint hp;
    NoPrint np;

    doPrint(hp); // 输出:HasPrint::print()
    doPrint(np); // 输出:Default print message

    return 0;
}

在这个例子中,我们定义了两个模板函数 doPrint,分别针对具有和没有 print() 成员函数的类型。通过使用 SFINAE 技术,编译器会根据实参的类型选择合适的函数模板进行实例化,从而实现条件选择。

泛型/元编程(非新特性)

补充整理、理解

泛型编程和元编程是 C++ 中的两个重要编程技术,它们都是为了实现更灵活、通用和高效的代码而设计的。

  1. 泛型编程: 泛型编程是一种编程范式,它强调编写通用代码,使得代码可以适用于不同类型的数据而无需针对每种类型编写特定的代码。在 C++ 中,泛型编程的主要工具是模板。通过使用函数模板和类模板,可以将代码与特定的数据类型解耦,从而实现通用性。

例如,考虑以下函数模板用于交换两个值:

template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

这个函数模板可以交换不同类型的数据,如 int、double、char、自定义类等,而无需为每种类型编写一个交换函数。

  1. 元编程: 元编程是一种编程技术,它允许在编译期间进行计算和操作,以生成更加复杂的代码或在编译期间优化代码。在 C++ 中,元编程的主要工具是模板元编程(Template Metaprogramming,TMP)。TMP 允许在编译期间进行模板实例化和递归展开,从而在编译时生成代码。

TMP 的一个典型应用是实现递归算法,通过模板的递归展开,在编译期间生成代码。 TMP 的一些特性,例如模板特化、constexpr、变量模板和编译期常量计算等,使得元编程在 C++ 中非常强大。

例如,以下代码使用 TMP 计算斐波那契数列:

template <int N>
struct Fibonacci {
    static constexpr int value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value;
};

template <>
struct Fibonacci<0> {
    static constexpr int value = 0;
};

template <>
struct Fibonacci<1> {
    static constexpr int value = 1;
};

int main() {
    constexpr int result = Fibonacci<5>::value; // 编译时计算斐波那契数列的第 5 项
    return 0;
}

在这个例子中,Fibonacci 结构体使用模板的特化和递归展开在编译期间计算斐波那契数列的值。这使得代码更加高效,因为在运行时不需要进行递归计算。

常见的元编程技术包括:

  • constexpr:用于编译期计算
  • 模板:用来进行静态多态
  • 递归:编译期递归计算
  • SFINAE:子语句失败不报错
  • 模板特化:自定义模板实现

但是元编程并不必须包含全部这些技术,其核心只是发生在编译期。

例如,可以只使用constexpr,不用递归和模板:

constexpr int factorial(int n) {
  return n <= 1 ? 1 : (n * factorial(n-1)); 
}

int main() {
  int x = factorial(5); // 120
}

这依然是编译期计算,但没有用到模板、特化等技术。

又例如可以只用模板,不用递归和constexpr:

template<int N>
struct Factorial {
  static const int value = N * Factorial<N-1>::value;
};

template<> 
struct Factorial<0> {
  static const int value = 1;
};

int main() {
  int x = Factorial<5>::value; // 120
}

元编程的手段很丰富,可以灵活组合。

泛型编程和元编程都是在编译时运行的,它们都是 C++ 中的编译期特性,即在编译阶段进行处理而不是运行时。这两种技术在编译时对代码进行处理,以实现代码的泛化、优化和生成更复杂的代码结构。

  1. 泛型编程:
    • 泛型编程是指在编写代码时不指定具体的类型,而使用参数化的类型,使得代码可以适用于不同类型的数据。在 C++ 中,泛型编程主要通过模板来实现。
    • 泛型编程在编译时进行类型推导和实例化,编译器会根据模板参数的类型生成对应的代码,从而实现代码的通用性和复用性。
  2. 元编程:
    • 元编程是一种在编译期间进行计算和代码操作的技术,它允许通过模板的特性在编译时生成代码。在 C++ 中,元编程主要通过模板元编程(Template Metaprogramming,TMP)来实现。
    • 元编程在编译时进行模板实例化和递归展开,通过在模板实例化过程中进行计算和条件判断来生成代码。元编程的目的通常是实现优化、生成复杂的代码结构、在编译时进行条件选择等。

因为泛型编程和元编程都在编译阶段进行处理,所以它们不会产生运行时的额外开销。这使得泛型编程和元编程成为 C++ 中非常强大的编程技术,可以在编译期间实现高度灵活和高效的代码。

标签:编译,int,代码,编程,特性,cpp14,关键,类型,模板
From: https://www.cnblogs.com/linxmouse/p/17568347.html

相关文章

  • 【我和openGauss的故事】openGauss特性:CM支持两节点部署特性
    【我和openGauss的故事】openGauss特性:CM支持两节点部署特性杨凯同学2023-07-1418:50发表于openGauss公众号1.什么是CMCM(ClusterManager)是一款数据库管理模块。支持自定义资源监控,提供了数据库主备的状态监控、网络通信故障监控、文件系统故障监控、故障自动主备切换等能力。......
  • 4项关键技术提升 XR 扩展现实体验-XR应用云流化
    无论是使用户能够协作设计电动赛车,还是帮助观众通过数字世界与自然互动,越来越多的企业利用XR扩展现实为用户提供沉浸式逼真的虚拟环境。下一代沉浸式技术的应用越来越广泛,图形和人工智能的最新突破正在扩展XR的功能。这四种技术正在XR生态系统中树立新标准:云流化,高级协作工具,高......
  • 解读 ---- yield 关键字
    合集-c#基础(6) 1.编码技巧---如何实现字符串运算表达式的计算07-122.编码技巧---同步锁对象的选定07-133.解读---yield关键字07-174.并发编程---信号量线程同步07-185.并发编程---为何要线程池化07-186.编码技巧---谨防闭包陷阱07-19收起 引言yie......
  • Synchronized关键字同步类方法
    要想解决“脏数据”的问题,最简单的方法就是使用synchronized关键字来使run方法同步,代码如下:publicsynchronizedvoidrun(){}   从上面的代码可以看出,只要在void和public之间加上synchronized关键字,就可以使run方法同步,也就是说,对于同一个Java类的对象实例,run方法......
  • 数据中心机房建设,务必确定这13个关键点
    下午好,我的网工朋友。关于机房、机架的相关内容,给你们说了不少。今天再给你补充个知识点,机房建设,要怎么做。熟悉机房建设的网工朋友可能都知道,一个全面的数据中心机房建设工程一般包括:综合布线、抗静电地板铺设、棚顶墙体装修、隔断装修、UPS、专用恒温恒湿空调、机房环境监控系统......
  • 【八股文 01】const 关键字
    1const含义被它修饰的值不能改变,是只读变量。必须在定义的时候就给它赋初值2const作用1、修饰变量,说明该变量不可以被改变2、修饰指针,分为指向常量的指针(pointertoconst)和自身是常量的指针(常量指针,constpointer)和前面两种的组合:指向常量的常指针3、修饰引用,指向常量的......
  • Java高并发编程的关键概念和技术,深入理解并成功应对高并发问题
    Java高并发编程的关键概念和技术,深入理解并成功应对高并发问题1.是什么是高并发?高并发指的是系统在同一时间点需要处理大量并发请求的能力。这些请求可能来自多个用户或者多个线程。在高并发环境下,传统的串行处理方式往往无法满足性能需求,因此需要采用并发编程来提高系统的吞吐......
  • c语言static关键字的用法
    今天的c语言学习中遇到了static关键字,由于对这个关键字的用法比较模糊,但是这个关键字的作用很大,以下是它的用法:一、修饰局部变量(1)用静态关键字static修饰的局部变量,在编译的过程中,会在数据区为该变量开辟空间,并对其进行初始化,如果代码中未对其进行初始化,则系统默认初始化......
  • 数据结构与算法:图有哪些关键核心知识点
    图是一种复杂的数据结构,它由顶点和边组成,可以表示任意两个数据元素之间的关系。图有以下一些基本概念和术语:图可以分为无向图和有向图,根据边是否有方向。图可以分为简单图和多重图,根据边是否重复或自环。图可以分为完全图和非完全图,根据任意两个顶点之间是否存在边或弧。图......
  • Eplan是什么软件?学习Eplan软件的几个关键要点
    EPLAN是一款电气计算机辅助设计软件。我是一名Eplan软件的学习者,最近在学习这个专业的电气设计软件时,总结了一些关键要点,希望能与大家分享。  1.熟悉软件界面和功能:首先,我们需要熟悉Eplan软件的界面和各种功能。了解软件的布局、菜单和工具栏,掌握基本的操作方法。可以通过......