【C++八股-第十一期】C++14 新特性
感谢花花,你必Offer
提纲:
👉 八股:
介绍一下 函数返回值类型推导
介绍一下lambda表达式中参数为auto的情况
介绍一下C++14中引入的变量模板
介绍一下C++14中引入的别名模板
介绍一下
constexpr
的限制
[[deprecated]]
标记在C++14的新功能介绍一下
二进制字面量
与整形字面量分隔符
介绍一下
std::shared_timed_mutex
与std::shared_lock
std::integer_sequence
std::exchange
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_mutex
与std::shared_lock
std::shared_timed_mutex
和 std::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++八股##面经#