当前位置:   article > 正文

C++ 11意想不到的新特性_c++ 可空类型

c++ 可空类型

        前几天总结了一下C++11常用的特性,其实常用特性已经大部分融入大家的习惯了。那么今天介绍那些不常用的特性。

目录

一、字符串字面量

二、统一初始化

三、noexcept关键字

四、元编程

五、删除函数

六、内联函数

七、可空类型

八、类型宏

九、函数宏 

十、可变长参数模板


一、字符串字面量

        你有没有这样的烦恼比如你想输出“\n”,在C++之前你只能使用转义符:

std::cout<<"\\n"<<std::endl

        有了C++字符串字面量你可以这样写了:

std::string n = R"(\n)";

        他可以将字符串原封不动的输出。

        一个具体的例子:

  1. #include <iostream>
  2. #include<string>
  3. std::string hello = "Hello, world!\n";
  4. std::string raw_hello = R"(Hello, world!\n)";
  5. int main()
  6. {
  7. std::cout<<hello<<std::endl;
  8. std::cout<<raw_hello<<std::endl;
  9. return 0;
  10. }

        在上面的代码中,字符串字面量 "Hello, world!\n" 将创建一个标准字符串,而原始字符串字面量 R"(Hello, world!\n)" 将创建一个原始字符串,其中反斜杠转义字符不会被处理。

        你还可义将其用在路径命名上,windows系统上一个使用路径字符串的例子:

  1. std::string path = "C:\\Windows\\System32\\";
  2. std::string path = "C:\\Windows\\System32";

        这个字符串是加了转义符号的,也就是\\前面的\为后面的\进行转义,是不是很反人性啊?所以C++11中可以这么写:

  1. std::string path = R"(C:\Windows\System32\)";
  2. std::string path = R"(C:\Windows\System32)";

        这是windows系统的路径,更符合我们的习惯。

二、统一初始化

        C++11引入了统一初始化,这是一种初始化对象的更简单和灵活的方法。它允许您使用花括号语法初始化对象,而无需使用显式的构造函数。

  1. // 创建一个vector并初始化为{1, 2, 3}
  2. std::vector<int> v = {1, 2, 3};
  3. // 创建一个pair并初始化为{1, "Hello"}
  4. std::pair<int, std::string> p = {1, "Hello"};
  5. // 创建一个map并初始化为{{1, "A"}, {2, "B"}}
  6. std::map<int, std::string> m = {{1, "A"}, {2, "B"}};

        统一初始化允许您更清晰和直观地初始化对象,同时也避免了错误和不必要的代码。它还可以与模板代码一起使用,以更方便地创建复杂的数据结构。

三、noexcept关键字

        C++11引入了noexcept关键字,它是一个标识符,用于说明函数不会抛出异常。noexcept函数可以在抛出异常时带来性能提升,因为编译器不需要生成代码来处理异常。

        举个例子:

  1. int add(int x, int y) noexcept
  2. {
  3. return x + y;
  4. }

        在上面的代码中,我们定义了一个名为“add”的函数,该函数接受两个int类型的参数并返回它们的和。该函数被标记为noexcept,表示它不会抛出任何异常。

        在C++17中,noexcept还可以用作一种特殊的类型标记,可用于实现更多的编译时优化。例如:

  1. template<typename T>
  2. void process(T&& data) noexcept(std::is_nothrow_move_constructible<T>::value)
  3. {
  4. // Do something with data
  5. }

        在上面的代码中,process函数是一个模板,它可以接受任何可移动构造的类型的数据。如果T类型可以noexcept移动构造,则process函数也将被标记为noexcept。这允许编译器在特定条件下进行更高级的优化。

        当函数被标记为 noexcept 时,编译器会假设该函数不会抛出任何异常,并在生成代码时做出适当的优化。例如,编译器可能不会生成代码来处理异常堆栈,因此可以获得更快的运行速度。        

四、元编程

        C++ 元编程是一种通过使用 C++ 语言的特性,以编写编译器本身或其他编译时工具的技术。元编程可以在编译时生成代码,从而减少人工编写代码的工作量,并提高代码的一致性和可维护性。

        举个例子,模板元编程可以用于生成类型安全的固定大小数组,例如:

  1. template<typename T, std::size_t N>
  2. struct Array {
  3. T data[N];
  4. };

        在上面的例子中,Array 模板类接受两个模板参数:T 和 N。T 表示数组存储的数据类型,N 表示数组的大小。模板类的实例可以用于存储固定大小的数组。

        另一个例子是使用模板元编程实现类型判断:

  1. template<typename T>
  2. struct IsPointer {
  3. static constexpr bool value = false;
  4. };
  5. template<typename T>
  6. struct IsPointer<T*> {
  7. static constexpr bool value = true;
  8. };

        在上面的例子中,我们定义了两个模板结构体:IsPointer 和 IsPointer<T*>。首先,IsPointer<T> 定义了 value 静态常量,默认为 false。然后,IsPointer<T*> 是 IsPointer<T> 的特化,定义了 value 静态常量,默认为 true。

        这样,我们就可以使用 IsPointer<T>::value 来判断 T 是否是指针类型。例如:

  1. int i;
  2. int *p = &i;
  3. std::cout << IsPointer<decltype(i)>::value << std::endl;
  4. std::cout << IsPointer<decltype(p)>::value << std::endl;

输出结果:

0

1

五、删除函数

        它可以使我们在编译期明确地说明某个函数不能被使用,举个例子,下面是一个简单的删除函数的例子:

  1. struct NoCopy {
  2. NoCopy() = default;
  3. NoCopy(const NoCopy&) = delete;
  4. NoCopy& operator=(const NoCopy&) = delete;
  5. };
  6. int main() {
  7. NoCopy nc1;
  8. NoCopy nc2 = nc1; // error: use of deleted function
  9. nc1 = nc2; // error: use of deleted function
  10. return 0;
  11. }

        另一个例子源于我的一个bug,我们使用delete删除函数来删除了类中的默认的copy构造函数:

C++函数传参的初步探索_Thomas_Lbw的博客-CSDN博客

 六、内联函数

        C++11 引入了强制内联关键字 inline 的新语法,它可以强制编译器将函数作为内联函数进行编译,从而提高函数调用的效率。

  1. inline int max(int a, int b) {
  2. return (a > b) ? a : b;
  3. }
  4. int main() {
  5. int a = 10, b = 20;
  6. int m = max(a, b);
  7. return 0;
  8. }

        但是不同C++编译器不保证内联,内敛函数内部逻辑应该简单。

七、可空类型

        C++11 引入了可空类型(nullable types)的概念,可以表示变量是否有值(不是 null 或空指针)。为此,C++11 引入了新的头文件 <optional>,它提供了 std::optional 类型,用于表示可空的值。

tips:

该特性并不是C++17的一部分。

  1. #include <iostream>
  2. #include <optional>
  3. int main() {
  4. std::optional<int> x;
  5. if (x) {
  6. std::cout << "x has value: " << *x << std::endl;
  7. } else {
  8. std::cout << "x has no value." << std::endl;
  9. }
  10. x = 42;
  11. if (x) {
  12. std::cout << "x has value: " << *x << std::endl;
  13. } else {
  14. std::cout << "x has no value." << std::endl;
  15. }
  16. return 0;
  17. }

八、类型宏

        C++11 允许开发人员定义类型宏,这类似于常规宏,但是可以用于定义类型。类型宏通常用于简化类型名称,使代码更具可读性。

  1. #include <iostream>
  2. typedef int MyInt;
  3. int main() {
  4. MyInt x = 42;
  5. std::cout << "x has value: " << x << std::endl;
  6. return 0;
  7. }

九、函数宏 

        函数宏是在预处理时由编译器预处理器替换的宏定义,它们可以用来实现宏代码替换或者实现简单的预处理代码。它们通过使用 #define 宏定义来实现。

  1. #include <iostream>
  2. #define MAX(x, y) ((x) > (y) ? (x) : (y))
  3. int main() {
  4. int a = 10;
  5. int b = 20;
  6. std::cout << MAX(a, b) << std::endl;
  7. return 0;
  8. }

十、可变长参数模板

        这个特性在工程中非常好用,值得深入研究,不过在本篇做个简单介绍吧。

        可变长参数模板是一种允许模板函数或模板类接受任意数量的参数的技术。它们通常用于实现变量参数列表的函数或类。

        以下是一个简单的示例,该示例实现了一个求平均值的模板函数:

  1. #include <iostream>
  2. #include <numeric>
  3. #include <vector>
  4. template<typename T>
  5. T average(T t) {
  6. return t;
  7. }
  8. template<typename T, typename... Args>
  9. double average(T first, Args... args) {
  10. std::vector<double> v = {static_cast<double>(first), static_cast<double>(args)...};
  11. return std::accumulate(v.begin(), v.end(), 0.0) / v.size();
  12. }
  13. int main() {
  14. std::cout << average(1, 2, 3, 4, 5) << std::endl;
  15. return 0;
  16. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/310277
推荐阅读
相关标签
  

闽ICP备14008679号