自动秒收录

增强探索 (探索C 更强大 14新特性 更高效的编程)


文章编号:174 / 更新时间:2023-11-08 13:02:25 / 浏览:
存储

c++14并没有太大的改动,就连官方说明中也指出,c++14相对于c++11来说是一个比较小的改动,但是在很大程度上完善了c++11,所以可以说c++14就是在c++11标准上的查漏补缺。

在2014年8月18日正式批准宣布,同年12月15日正式发布release版本。本文中将就变动部分做一个总结,有需要改进和提升的地方希望大家批评指正。

一、C++14新特性

1.1新的语言特性

  • 变量模板
  • 泛型 lambda
  • lambda 初始化捕获
  • 新建/删除省略
  • 放宽对 constexpr 函数的限制
  • 二进制文字
  • 数字分隔符
  • 函数的返回类型推导
  • 具有默认非静态成员初始值设定项的聚合类。

1.2新库功能

  • std::make_unique
  • std::shared_timed_mutexstd::shared_lock
  • std::整数序列
  • 标准::交换
  • std::引用
  • 以及对现有图书馆设施的许多小改进,例如
  • 某些算法的两范围重载
  • 类型特征的类型别名版本
  • 用户定义的basic_string持续时间复杂的文字
  • ETC。

1.3变量模板

及之前,我们只有针对类和函数的模板。C++14中,新增了变量模板:

template
constexpr T pi = T(3.1415926535897932385L);
template
T circular_area(T r)
  return pi *r * r;

变量模板同样可以在类变量中使用:

template
class X {
  static T s;
template
T X::s = 0;

类似函数和类模板,当变量模板被引用时,则会发生实例化。

1.4lambda 表达式的新增功能

1)泛化

支持在lambda表达式中使用auto定义变量类型:

#include 
#include 
using namespace std;
int main()
  auto glambda = [](auto& a) { cout << a << " "; };
  int a[] = { 4, 2, 6, 3, 7, 5 };
  for_each(a, a + sizeof(a) / sizeof(int), glambda);
  cout << endl;

2)对捕获的变量和引用进行初始化

include 
using namespace std;
int main()
  int x = 4;
  auto y = [&r = x, x = x + 1]()->int
    r += 2;
    return x * x;
  cout << "x = " << x << " y = " << y << endl;

1.5constexpr函数可以包含多个语句

在C++11中,如果想使用constexpr方法,只能包含一个返回语句。C++14中,放宽了此要求,允许constexpr函数中声明变量,使用循环和条件语句等:

#include 
#include 
using namespace std;
constexpr bool isPrimitive(int number)
  if (number <= 0)
    return false;
  for (int i = 2; i <= sqrt(number) + 1; ++i)
    if (number % i == 0)
      return false;
  return true;
int main()
  cout << boolalpha << isPrimitive(102) << " " << isPrimitive(103);
  return 0;

在C++11中,我们一般需要通过递归来实现相同的功能:

constexpr bool isPrimitive(int number, int currentFactor, int maxFactor)
  return currentFactor == maxFactor ? true : 
      (number % currentFactor == 0 ? false : 
        isPrimitive(number, currentFactor + 1, maxFactor));
constexpr bool isPrimitive(int number)
  return number <= 0 ? false : isPrimitive(number, 2, sqrt(number) + 1);

1.6整型

1)二进制字面量

支持使用 0b 开头的一串数字作为二进制表示的整型:

int a = 0b10101001110; // 1358

2)数字分割符

支持在数字中使用单引号进行分割(便于阅读)。在编译时,这些单引号会被忽略。

int a = 123'456'789; // 123456789

1.7返回类型自动推导

在C++14中,我们可以使用auto作为函数返回值并且不需要指明其返回类型的推导表达式

int x = 1;
auto f() { return x; }
/* c++11
auto f() -> decltype(x) { return x; } 

这种类型推导有一些限制:

  • 一个函数中所有返回字句必须推导出相同的类型;
  • 使用 {} 包裹的数据作为返回值时,无法推导其类型;
  • coroutine不能被推导;
  • 函数模板中可以使用类型推导,但是其显式实例化和特化版本必须使用相同的返回类型描述符。

1.8exchange

exchange用于移动语义,可以使用指定的新值替换掉原值,并返回原值。其定义在C++20中被简单修改如下:

template
constexpr // since C++20
T exchange(T& obj, U&& new_value)
    T old_value = std::move(obj);
    obj = std::forward(new_value);
    return old_value;

其使用如下:

#include 
#include 
#include 
using namespace std;
int main()
  vector v = {5, 6, 7};
  std::exchange(v, { 1,2,3,4 });
  std::copy(begin(v), end(v), ostream_iterator(cout, " "));
  cout << endl;

1.9quoted

该类用于字符串转义的处理。使用 out << quoted(s, delim, escape) 的形式,可以将字符串 s 的转义格式写入输出流中;使用 in >> quoted(s, delim, escape) 可以将输入流去除转义格式后写入字符串 s 中。其中,delim 指明了需要转义的字符,escape 指明了修饰该转移字符的字符:

#include 
#include 
#include 
using namespace std;
int main()
  stringstream ss;
  string in = "String with spaces, and embedded \"quotes\" too";
  string out;
  auto show = [&](const auto& what) 
    &what == &in
      ? cout << "read in     [" << in << "]\n"
      << "stored as   [" << ss.str() << "]\n"
      : cout << "written out [" << out << "]\n\n";
  ss << quoted(in); 
  show(in);
  ss >> quoted(out);
  show(out);
  ss.str(""); 
  in = "String with spaces, and embedded $quotes$ too";
  const char delim{ '$' };
  const char escape{ '%' };
  ss << quoted(in, delim, escape);
  show(in);
  ss >> quoted(out, delim, escape);
  show(out);

二、C++14经常考到的知识点

2.1C++14引入了哪些新特性?

C++14引入了一些新特性,包括但不限于以下内容:

  1. 通用:允许在lambda函数中使用auto关键字来推导参数类型。
  2. 自动返回类型推导:允许使用auto关键字自动推导函数返回值类型。
  3. 初始化列表的泛型支持:可以使用auto关键字在初始化列表中推导元素类型。
  4. 带有二进制分隔符的整数字面量:可以在整数常量中使用单撇号作为分隔符,提高可读性。
  5. constexpr函数的扩展:constexpr函数可以包含更多操作,例如循环和条件判断。
  6. 变长参数模板(Variadic Templates)的改进:支持递归处理变长参数模板的展开。
  7. 返回void类型的lambda表达式:允许定义返回void类型的lambda函数。

2.2C++14中auto关键字的用法和限制是什么?

在C++14中,auto关键字用于自动类型推导,可以根据初始化表达式的类型来确定变量的类型。它的使用和限制如下:

  1. 自动类型推导:使用auto关键字声明变量时,编译器会根据初始化表达式的类型自动推导出变量的类型。
    auto x = 42;// 推导为int型auto name = "John";// 推导为const char*型
  2. 声明时必须初始化:使用auto声明变量时,必须进行初始化。因为编译器需要根据初始化表达式来推导出变量的类型。
    auto y;// 错误,未初始化
  3. 可与引用结合使用:auto关键字可以与引用一起使用,从而推导出引用的类型。
    int a = 10; auto& ref = a;// 推导为int&型,ref是a的引用
  4. 不支持数组或函数指针:auto不能直接用于数组或函数指针的声明。但可以通过decltype结合auto来实现对数组或函数指针类型进行推导。
    int arr[] = {1, 2, 3}; auto arrRef = arr;// 错误,无法推导arr的数组类型decltype(arr) arrType;// 使用decltype获取arr的数组类型并声明arrTypevoid foo(); auto funcPtr = foo;// 错误,无法推导foo的函数指针类型decltype(foo)* funcPtrType;// 使用decltype获取foo的函数指针类型并声明funcPtrType

需要注意的是,auto在C++14中的用法和限制可能与之后的标准(如C++17、C++20等)有所不同,具体取决于编译器和所使用的标准版本。

2.3C++14中如何使用Lambda表达式?有什么改进?

在C++14中,使用Lambda表达式的语法与之前的C++版本相似。Lambda表达式是一种可以在代码中内联定义匿名函数的方式。

下面是一个使用Lambda表达式的示例:

#include 
#include 
#include 
int main() {
    std::vector numbers = {1, 2, 3, 4, 5};
    // 使用Lambda表达式进行遍历打印
    std::for_each(numbers.begin(), numbers.end(), [](int num) {
        std::cout << num << " ";
    return 0;

在Lambda表达式中,方括号用于捕获外部变量(可选)。小括号内指定参数列表(可选),箭头后面指定返回类型(可选)。

C++14对于Lambda表达式有一些改进,其中最显著的改进是可以自动推导返回类型。这意味着你不需要显式地指定返回类型,编译器会根据表达式体来推断返回类型。

以下是一个示例:

auto lambda = [](int a, int b) {
    return a + b;

在上述示例中,我们没有显式指定返回类型,但编译器会自动推断出返回类型为整数(因为a和b都是整数)。

此外,在C++14中还引入了泛型lambda,使得可以在lambda函数中使用auto关键字作为参数类型,更加灵活和方便。

2.4C++14对于constexpr关键字有何改进?

C++14对于constexpr关键字进行了一些改进,使得其更加灵活和强大。在C++11中,constexpr只能用于表示常量表达式的函数和构造函数,而在C++14中,它还可以用于一些额外的情况。

首先,在C++14中,constexpr函数可以包含一些非常量表达式的逻辑,只要这部分逻辑在运行时不会执行即可。这意味着我们可以在constexpr函数内使用循环、条件语句等非常量表达式的控制流程。

其次,C++14引入了对变量模板(Variable Templates)的支持,并且允许将变量声明为constexpr。这样我们就可以定义并初始化一个编译期间可计算的常量变量。

此外,在C++14中,对于某些标准库类型(如数组、字符串等),它们也提供了更多的支持以便于使用在编译期间计算出来的常量值。

2.5C++14中提供了哪些新的标准库组件和功能?

C++14引入了一些新的标准库组件和功能,以下是其中的一些主要特性:

  1. std::make_unique:提供了在堆上创建 unique_ptr 对象的便捷方式。
  2. std::integer_sequence:支持编译时整数序列的操作,用于元编程。
  3. std::user_defined_literals:允许用户定义自己的字面量后缀,扩展了语言的表达能力。
  4. 通用 lambda 表达式:允许使用 auto 参数声明参数类型,使得 lambda 表达式更加灵活。
  5. 变长模板参数折叠(Variadic template parameter packs expansion):可以将多个参数打包传递给模板函数或类,并且可以对它们进行展开操作。
  6. std::experimental 命名空间:引入了一些实验性质的标准库组件,如 optional、any、string_view 等。

2.6在C++14中,变长参数模板是如何使用的?

在C++14中,可以使用变长参数模板(Variadic Templates)来处理可变数量的函数参数。通过使用递归展开参数包的方式,可以灵活地处理任意数量的参数。

下面是一个示例:

#include 
// 递归终止条件:当没有剩余参数时停止递归
void printArgs() {
    std::cout << "All arguments have been printed." << std::endl;
// 可变参数模板:展开第一个参数并调用自身处理剩余参数
template
void printArgs(T first, Args... args) {
    std::cout << "Argument: " << first << std::endl;
    printArgs(args...); // 递归调用自身处理剩余参数
int main() {
    printArgs(1, "Hello", 3.14, 'A');
    return 0;

输出结果:

Argument: 1
Argument: Hello
Argument: 3.14
Argument: A
All arguments have been printed.

在上述代码中,printArgs是一个可变参数模板函数。它首先处理第一个传入的参数first,然后递归地调用自身处理剩余的args参数。当所有参数都被展开并打印完毕后,最终会到达递归终止条件。

这种方式使得我们能够在编译时处理不同数量和类型的函数参数,并且可以灵活地进行操作。

2.7在C++14中,是否允许在lambda函数内定义其他函数或类?

在C++14中,lambda函数内是不允许定义其他函数或类的。Lambda函数是一个匿名的函数对象,它通常用于简化代码,提供一种在局部范围内编写小型函数的方式。Lambda函数本质上是一个闭包,它可以捕获外部作用域中的变量,并且具有与普通函数相似的行为。

然而,在C++17中引入了嵌套lambda的概念,使得在lambda函数内定义其他lambda函数成为可能。在这种情况下,内层的lambda函数可以访问外层lambda函数的变量。所以如果你想要在C++14中定义其他函数或类,建议将其定义在lambda之外的范围内。

2.8C++14是否支持原始字符串字面量(raw string literals)?如何使用它们?

是的,C++14支持原始字符串字面量(raw string literals)。

原始字符串字面量可以用来表示包含特殊字符(例如转义序列和引号)的字符串,而无需使用转义符号。它们由R"delim(raw_characters)delim"的语法表示,其中delim可以是任何非空字符序列,并且在开始和结束位置上必须匹配。

以下是一个示例:

#include 
int main() {
    const char* str1 = R"(Hello \n World!)";
    std::cout << str1 << std::endl;  // 输出:Hello \n World!
    const char* str2 = R"###(This is a "quoted" string.)###";
    std::cout << str2 << std::endl;  // 输出:This is a "quoted" string.
    return 0;

在上面的示例中,我们使用了原始字符串字面量来创建包含特殊字符的字符串,而不需要使用额外的转义符号。

2.9在C++14中,std::make_unique和std::make_shared这两个函数的作用是什么?

在C++14中,std::make_uniquestd::make_shared是用于创建智能指针的函数模板。

  • std::make_unique:用于创建一个std::unique_ptr对象,它拥有独占所有权的动态分配对象。这个函数接受参数并返回一个std::unique_ptr,它会自动管理内存释放。示例:
auto ptr = std::make_unique(42);
  • std::make_shared:用于创建一个std::shared_ptr对象,它可以被多个指针共享的动态分配对象。这个函数接受参数并返回一个std::shared_ptr,它使用引用计数来管理内存释放。示例:
auto ptr = std::make_shared(42);

这两个函数可以减少手动进行资源管理的工作量,并提供了更安全、更简洁的方式来处理动态分配对象。

2.10C++14引入了统一初始化语法(uniform initialization syntax),具体有哪些变化?

C++14引入了统一初始化语法(uniform initialization syntax),它允许使用一种更统一和一致的方式进行初始化。具体的变化包括以下几个方面:

  1. 初始化列表(initializer list):可以使用花括号来初始化对象,无论是简单类型还是复杂类型。例如:
    int num{ 42 }; std::vector vec{ 1, 2, 3 };
  2. 自动类型推导:在使用统一初始化语法时,编译器可以自动推导出变量的类型。
    auto value{ 3.14 };// 推导为 double 类型auto str{ "Hello" };// 推导为 const char[6] 类型
  3. 统一构造函数调用语法:通过统一初始化语法,可以直接调用类的构造函数进行对象的创建。
    class MyClass { public: MyClass(int value) {/* 构造函数实现 */}}; MyClass obj{ 42 };// 调用构造函数创建对象
  4. 空初始化:可以使用或进行空初始化,不再需要显式地指定默认值。
    int num{};// 初始化为0std::string str{};// 初始化为空字符串

这些变化使得初始化更加灵活和一致,并且提供了更强大的类型推导能力。注意,在使用统一初始化语法时,要注意类型的精确匹配和可能的隐式转换。

三、C++14 新特性总结

C++14 这一继 C++11 之后的新的 C++ 标准已经被正式批准,正在向ISO 提交,将于年内发布。C++ 之父 Bjarne Stroustrup 说道,尽管与 C++11 相比,C++14 的改进“有意做的比较小”,但是仍然为用户“带来了极大的方便”,是实现使C++“对新手更为友好”这一目标的步骤之一

在C++ 的时间表中,C++14 按计划是一个小版本,完成制定 C++11 标准的剩余工作,目的是使 C++ 成为一门更清晰、更简单和更快速的语言。新的语言特性留到了未来的 C++17 标准中。


C++14 的主要特性可以分为三个领域:Lambda 函数、constexpr 和类型推导。

3.1Lambda 函数

C++14 的泛型 Lambda 使编写如下语句成为可能:

auto lambda = [](auto x, auto y) {return x + y;};

而另一方面,C++11 要求 Lambda 参数使用具体的类型声明,比如:

auto lambda = [](int x, int y) {return x + y;};

此外,新标准中的 std::move 函数可用于捕获 Lambda 表达式中的变量,这是通过移动对象而非复制或引用对象实现的:

std::unique_ptr ptr(new int(10));
auto lambda = [value = std::move(ptr)] {return *value;};

(1)lambda参数auto

在c++11中,lambda表达式参数需要使用具体的类型声明。

auto f = [] (int a) { return a; }

在c++14中,lambda表达式参数可以直接为auto。

auto f = [] (auto a) { return a; };
cout << f(1) << endl;
cout << f(2.3f) << endl;

3.2constexpr

在 C++11 中,使用 constexpr 声明的函数可以在编译时执行,生成一个值,用在需要常量表达式的地方,比如作为初始化模板的整形参数。C++11 的 constexpr 函数只能包含一个表达式,C++14 放松了这些限制,支持诸如 if 和 switch 等条件语句,支持循环,其中包括基于区间(range)的 for 循环。

(1)变量模板

c++14支持变量模板。

template
constexpr T pi = T(3.1415926535897932385L);
int main() {
    cout << pi << endl; // 3
    cout << pi << endl; // 3.14159
    return 0;

(2)别名模板

c++14支持别名模板。

template
struct A {
    T t;
    U u;
template
using B = A;
int main() {
    B b;
    b.t = 10;
    b.u = 20;
    cout << b.t << endl;
    cout << b.u << endl;
    return 0;

(3)constexpr的限制

c++14相较于c++11减少了限制:c++11和c++14中constexpr函数均可以使用递归。

constexpr int factorial(int n) { // C++14 和 C++11均可
    return n <= 1 ? 1 : (n * factorial(n - 1));

c++14还可以使用局部变量和循环。

constexpr int factorial(int n) { // C++11中不可,C++14中可以
    int ret = 0;
    for (int i = 0; i < n; ++i) {
        ret += i;
    return ret;

c++11中constexpr函数中必须把所有东西放在一个单独的return语句中。

constexpr int func(bool flag) { // C++14 和 C++11均可
    return 0;

c++14中constexpr函数没有上述限制。

constexpr int func(bool flag) { // C++11中不可,C++14中可以
    if (flag) return 1;
    else return 0;

3.3类型推导

C++11 仅支持 Lambda 函数的类型推导,C++14 对其加以扩展,支持所有函数的返回类型推导:

auto DeducedReturnTypeFunction();

因为 C++14 是强类型语言,有些限制需要考虑:

  • 如果一个函数的实现中有多个返回语句,这些语句一定要推导出同样的类型。
  • 返回类型推导可以用在前向声明中,但是在使用它们之前,翻译单元中必须能够得到函数定义。
  • 返回类型推导可以用在递归函数中,但是递归调用必须以至少一个返回语句作为先导,以便编译器推导出返回类型。

C++14 带来的另一个类型推导方面的改进是 decltype(auto) 语法,它支持使用与 auto 同样的机制计算给定表达式的类型。auto 和 decltype 在 C++11 中就已经出现了,但是它们在推导类型时使用了不同的机制,这可能会产生不同的结果。

C++14 中的其他改变包括可以声明变量模板,支持使用 0b 或 0B 前缀来声明二进制字面常量。InfoQ 已经介绍过C++14 中可能破坏 C++11 程序的其他小型修改

主流 C++ 编译器对新语言特性的支持正在有条不紊地开发:Clang“完全实现了当前草案的所有内容”;GCCVisual Studio也对 C++14 的新特性提供了一些支持。

(1)函数返回值类型推导

c++14对函数返回类型推导规则做了优化:

auto func(int i) {  //C++11编译非法,c++14支持auto返回值类型推导
    return i;
int main() {
    cout << func(4) << endl;
    return 0;

支持函数模块返回值类型推导:

template
auto func(T t) { return t; }
int main() {
    cout << func(4) << endl;
    cout << func(3.4) << endl;
    return 0;

auto返回值用例:

// 编译失败,多个return语句必须返回相同类型。
auto func(bool flag) {
    if (flag) return 1;
    else return 2.3;
// 编译失败,不能返回初始化列表
auto func1() {
    return {1, 2, 3};
//虚函数不能返回类型推导
class A{
    virtual auto func() { return 1; }
//返回值类型推导可以提前前声明,但使用前必须进行函数定义。
auto f();               // declared, not yet defined
auto f() { return 42; } // defined, return type is int
// 回类型推导可以用在递归函数中,但是递归调用必须以至少一个返回语句作为先导,以便编译器推导出返回类型。
auto sum(int i) {
    if (i == 1)
        return i;              // return int
    else
        return sum(i - 1) + i; // ok
int main() {
    cout << f() << endl;
    return 0;

3.4C++14其他

(1)[[deprecated]]标记

c++14中增加deprecated标记,修饰类、变量、函数等。编译时产生警告,提醒用户该标记修饰的内容未来可能会被丢弃。

struct [[deprecated]] A { };
int main() {
    A a;
    return 0;

(2)二进制字面量与字面量分隔符

c++14引入了二进制字面量和字面量分隔符。

int a = 0b0001'0011'1010;
double b = 3.14'1234'1234'1234;

(3)std::make_unique

c++11中有std::make_shared,c++14增加了std::make_unique。

struct A {};
std::unique_ptr ptr = std::make_unique();

(4)std::shared_timed_mutex与std::shared_lock

c++14通过std::shared_timed_mutex和std::shared_lock来实现读写锁,保证多个线程可以同时读,但是写线程必须独立运行,写操作和读操作不可同时进行,这种情况下才能从shared_mutex中获取性能优势。

c++11 中互斥量

c++14互斥量管理类-锁

通常这样定义:

实现方式:

(5)std::integer_sequence

表示一个编译时的整型序列。

(6)std::exchange

以 new_value 替换 obj 的值,并返回 obj 的旧值。注意与std::swap的区别。

(7)std::quoted

c++14引入std::quoted用于给字符串添加双引号。

精品文章推荐阅读:


相关标签:
网络协议存储Linux编译器C

本文地址:https://www.0558.la/article/b91a358cc01b497dcef3.html

上一篇:tibetan字典Trie字典树...
下一篇:新注册的域名多久可以备案新注册的域名要如...

温馨提示

做上本站友情链接,在您站上点击一次,即可自动收录并自动排在本站第一位!
<a href="https://www.0558.la/" target="_blank">自动秒收录</a>