【C++八股-第十一期】C++14 新特性

感谢花花,你必Offer

提纲:

👉 八股:

  1. 介绍一下 函数返回值类型推导

  2. 介绍一下lambda表达式中参数为auto的情况

  3. 介绍一下C++14中引入的变量模板

  4. 介绍一下C++14中引入的别名模板

  5. 介绍一下 constexpr 的限制

  6. [[deprecated]]标记在C++14的新功能

  7. 介绍一下 二进制字面量整形字面量分隔符

  8. 介绍一下std::shared_timed_mutexstd::shared_lock

  9. std::integer_sequence

  10. std::exchange

  11. std::quoted

1. 介绍一下 函数返回值类型推导

在 C++14 中引入了函数返回值类型推导(Return Type Deduction for Normal Functions),这个特性允许函数的返回类型可以根据返回语句推导出来,而不必显式地指定返回类型。这种功能类似于 C++11 中引入的 auto 关键字用于变量声明中的类型推导,但应用于函数的返回类型。

函数返回值类型推导的语法:

在 C++14 中,可以使用 auto 关键字来推导函数的返回类型。基本的语法形式如下:

auto functionName(parameters) {
    // 函数体
    return expression;
}

注意:

① 函数内如果有多个return语句,它们必须返回相同的类型,否则编译失败。

auto func(bool flag) {
    if (flag) return 1;
    else return 2.3; // 报错
}
// 自动返回类型的推导不一致:先是'int'然后是'double'

② 如果return语句返回初始化列表,返回值类型推导也会失败

auto func() {
    return {1, 2, 3}; // 报错
}

③ 如果函数是虚函数,不能使用返回值类型推导

struct A {
    // 错误:虚函数无法推导返回类型
    virtual auto func() { return 1; } 
}

④ 返回类型推导可以用在前向声明中,但是在使用它们之前,当前生命周期内必须能够得到函数定义

auto f();               // 声明,未定义
auto f() { return 42; } // 定义,返回值为int

int main() {
    cout << f() << endl;
}

⑤ 返回类型推导可以用在递归函数中,但是递归调用必须以至少一个返回语句作为先导,以便编译器推导出返回类型。

auto sum(int i) {
    if (i == 1)
        return i;              // 告诉编译器返回值是int
    else
        return sum(i - 1) + i; // 不报错
}

2. 介绍一下lambda表达式中参数为auto的情况

C++11中,lambda表达式参数需要使用具体的类型声明:(注意下面的* * 不是代码内容,只是为了重点标记所说的位置)

auto f = [] (**int** a) { return a; }
//实际代码: 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. 介绍一下C++14中引入的变量模板

变量模板(Variable Templates)这个特性,允许我们像模板函数一样定义和使用变量模板,使得我们可以以模板的方式定义和初始化变量。

变量模板的基本语法:

template <typename T>
constexpr T variable_name = initial_value;
  • template <typename T>:定义模板,T 是模板参数类型。
  • constexpr T variable_name:定义变量模板,variable_name 是变量模板的名称,T是变量模板的类型。变量模板通常使用 constexpr 修饰,使得其在编译时就可以求值,提升性能和安全性。
  • = initial_value:可选的初始化值。
使用示例:
#include <iostream>

// 定义变量模板
template <typename T>
constexpr T pi = T(3.1415926535897932385);

int main() {
    // 使用变量模板
    std::cout << "Value of pi (float): " << pi<float> << std::endl;
    std::cout << "Value of pi (double): " << pi<double> << std::endl;
    std::cout << "Value of pi (long double): " << pi<long double> << std::endl;

    return 0;
}

4. 介绍一下C++14中引入的别名模板

别名模板允许我们为模板实例化提供一个简洁的名称,别名模板使得对复杂类型或模板实例化的引用更加简洁和可读。

别名模板的基本语法:

template <typename T>
using alias_name = underlying_type<T>;
  • alias_name 是别名模板的名称
  • underlying_type<T> 是模板参数 T 对应的实际类型或者类型表达式。
示例:
template<typename T, typename U>
struct A {
    T t;
    U u;
};

template<typename T>
using B = A<T, int>;

int main() {
    B<double> b;
    b.t = 10;
    b.u = 20;
    cout << b.t << endl;
    cout << b.u << endl;
    return 0;
}

5. 介绍一下 constexpr 的限制

constexpr是在 C++11 中引入的,是用于声明常量表达式的关键字,它告诉编译器表达式可以在编译期间求值,从而提高程序的性能和安全性。

C++14相较于C++11 扩展了 constexpr 函数的能力,允许更多的控制流结构和局部变量的使用,使得 constexpr 函数在编译期间能够执行更多的计算

C++11 中的 constexpr 函数:

在 C++11 中,constexpr 函数只能包含一条 return 语句,并且不能包含局部变量或循环等复杂的控制流语句。因此,递归是唯一允许的控制流结构,因为递归可以展开为一系列嵌套的函数调用,形成一条直接的计算路径。

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

C++14 中的 constexpr 函数:

在 C++14 中,constexpr 函数的能力得到了扩展,允许包含局部变量和有限的循环(循环次数在编译期间可以确定的情况下)。这意味着,像下面示例中的循环计算阶乘的例子,在 C++14 中是允许的,但在 C++11 中是不允许的。

constexpr int factorial(int n) {
    int ret = 1;
    for (int i = 1; i <= n; ++i) {
        ret *= i;
    }
    return ret;
}

return数量问题

在 C++11 中,constexpr 函数只能包含一条 return 语句;

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

在 C++14 中,constexpr 函数允许多条 return 语句,但所有分支都必须返回相同的值。

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

6. [[deprecated]]标记在C++14的新功能

[[deprecated]] 是 C++11 引入的一种属性标记,用于标识已被废弃的特性、函数、类或变量。它的作用是告诉编译器和其他开发者,某个特定的代码实体不推荐再使用,因为它可能会被未来版本移除或者有更好的替代方案。

使用方法:

在 C++11 中,可以通过在声明之前加上 [[deprecated]] 属性标记来将一个实体标记为已废弃,如下所示

[[deprecated]] void oldFunction(); // 声明一个已废弃的函数

在 C++14 中,还可以指定一个字符串,用于提供额外的说明或替代建议:

[[deprecated("Use newFunction() instead")]] void oldFunction();

示例:

[[deprecated("Use newFunction() instead")]] void oldFunction();

int main() {
    oldFunction(); // 编译器会发出已废弃警告
    return 0;
}

7. 介绍一下 二进制字面量整形字面量分隔符

旨在提高代码的可读性和书写灵活性

二进制字面量

二进制字面量允许直接表示二进制数字,在 C++14 中,可以使用前缀 0b 或 0B 来表示二进制数。

int binaryNumber = 0b1010; // 二进制数 1010,等同于十进制的 10

整数字面量分隔符

整数字面量分隔符允许在长整数中使用单引号 ' 来增强可读性,分隔符可以插入在数字中间,但不能在数字开头或结尾。

int bigNumber = 1'000'000; // 一百万,增强了数字的可读性

8. 介绍一下std::shared_timed_mutexstd::shared_lock

std::shared_timed_mutexstd::shared_lock 是 C++14 引入的用于实现共享(读写)锁机制的同步原语。它们允许多个线程同时读取资源,而在写入资源时则需要独占访问权限。这种机制可以提高并发性能,尤其是在读多写少的情况下。

std::shared_timed_mutex

std::shared_timed_mutex 是一种共享互斥锁,它允许多个线程同时获得共享锁(即读取权限),但只有一个线程能够获得独占锁(即写入权限),获取独占锁时会阻塞其他线程的共享锁和独占锁的获取。

#include <iostream>
#include <thread>
#include <shared_mutex>

std::shared_timed_mutex sharedMutex;

void readData(int id) {
	//获取共享锁进行读操作
    std::shared_lock<std::shared_timed_mutex> lock(sharedMutex);
    std::cout << "Thread " << id << " is reading the data." << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

void writeData(int id) {
	//获取独占锁进行写操作
    std::unique_lock<std::shared_timed_mutex> lock(sharedMutex);
    std::cout << "Thread " << id << " is writing the data." << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

int main() {
    std::thread readers[5];
    std::thread writer(writeData, 0);

    for (int i = 0; i < 5; ++i) {
        readers[i] = std::thread(readData, i + 1);
    }

    writer.join();
    for (int i = 0; i < 5; ++i) {
        readers[i].join();
    }

    return 0;
}

9. std::integer_sequence

主要特性和用途

std::integer_sequence 提供了一种在编译时处理整数序列的方法,可以用来生成和操作整数序列。这在需要展开或处理参数包的场景下非常有用,如在编写泛型编程和模板元编程时。

生成整数序列

你可以使用 std::make_integer_sequence 来生成一个从 0 到 N-1 的整数序列:

#include <iostream>
#include <utility>

template <std::size_t... Is>
void print_indices(std::integer_sequence<std::size_t, Is...>) {
    ((std::cout << Is << " "), ...);
    std::cout << std::endl;
}

int main() {
    constexpr std::size_t N = 5;
    print_indices(std::make_integer_sequence<std::size_t, N>{});
    return 0;
}

展开参数包 - 和std::tuple的配合使用

#include <iostream>
#include <tuple>
#include <utility>

template <typename Tuple, std::size_t... Is>
void print_tuple(const Tuple& t, std::index_sequence<Is...>) {
    ((std::cout << std::get<Is>(t) << " "), ...);
    std::cout << std::endl;
}

int main() {
    auto t = std::make_tuple(1, 2.5, "hello");
    print_tuple(t, std::make_index_sequence<std::tuple_size<decltype(t)>::value>{});
    return 0;
}

10. std::exchange

位于头文件 <utility> 中。它的主要用途是将一个对象的值替换为一个新值,并返回该对象的旧值。

在重置智能指针时使用 std::exchange

#include <iostream>
#include <memory>
#include <utility>

void resetPointer(std::unique_ptr<int>& ptr) {
    auto old_ptr = std::exchange(ptr, nullptr); // 将 ptr 设置为 nullptr,并返回旧的指针
    if (old_ptr) {
        std::cout << "Resetting pointer with value: " << *old_ptr << std::endl;
    }
}

int main() {
    auto ptr = std::make_unique<int>(42);
    std::cout << "Pointer value: " << *ptr << std::endl; // 输出: 42

    resetPointer(ptr); // 重置指针
    if (!ptr) {
        std::cout << "Pointer is now null" << std::endl; // 输出: Pointer is now null
    }

    return 0;
}

11. std::quoted

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

int main() {
    string str = "hello world";
    cout << str << endl;
    cout << std::quoted(str) << endl;  //  "hello world"
    return 0;
}
#C++##C++八股##面经#
全部评论

相关推荐

1 2 评论
分享
牛客网
牛客企业服务