C++17新特性,c++17好用的新特性总结

c++ c++ 1558 人阅读 | 0 人回复

发表于 2022-11-25 22:24:59 | 显示全部楼层 |阅读模式

前项目C++使用的时C++14标准,考虑到与时俱进,C++20也基本上确定,不过由于目前使用的linux(uos)上的自带gcc并不能全面支持C++20, 所以暂时考虑新项目上C++17新标准,并且写一篇完整的C++17中项目可能用到的新特性简单文档

visual studio 2019需要添加编译参数 /std:c++17
If Statements with Initializer
带初始化器的if语句
example
  1. std::map<std::string, int> map;
  2. map["nihao"] = 1;
  3. map["shijie"] = 2;
  4. if (auto ret = map.begin(); ret != map.end()) {
  5.   std::cout << ret->first << ": " << ret->second;
  6. }
复制代码
Constexpr if
在 constexpr if 语句中,条件 的值必须是可按语境转换到 bool 类型的经转换常量表达式。若其值为 true,则舍弃 false分支语句(若存在),否则舍弃 true分支语句。
通常我们写业务很难用到,在模板元编程中会特别有用
example
  1. template <typename T>
  2. void TestConstexprIf(T value) {
  3.     if constexpr (std::is_integral_v<T>)
  4.         std::cout << "is integral" << std::endl;
  5.     else
  6.         static_assert(false, "T必须是整型");
  7. }
复制代码
inline 变量
inline 变量用来解决一个问题,全局变量再头文件中定义之后多出使用产生符号重定义错误
错误例子
  1. // test.h 头文件
  2. int test = 10;

  3. // test1.cpp
  4. void Function1() {
  5.   test = 20;
  6. }

  7. // test2.cpp
  8. void Function() {
  9.   test = 30;
  10. }


  11. // 上面的代码编译将会产生重定义错误,c++17之前解决方案是使用extern导出全局变量

  12. // 解决方案

  13. // test.h 头文件
  14. extern int test;

  15. // test.cpp
  16. int test = 10;
复制代码
C++17 之后引入inline变量使其全局变量可以直接再头文件中声明定义
example
inline int test = 10;嵌套命名空间
example
  1. namespace test::test2 {
  2. int i = 0;
  3. }

  4. std::cout << test::test2::i << std::endl;
复制代码
属性说明符
属性为各种由实现定义的语言扩展(例如 GNU 与 IBM 的语言扩展 __attribute__((...)),微软的语言扩展 __declspec() 等)提供了统一化的语法。
[[fallthrough]] 标准属性
指示从前一标号直落是有意的,而在发生直落时给出警告的编译器不应诊断它。
C++17 之前的标准下, 有如下代码
  1. switch (device.status())
  2. {
  3. case sleep:
  4.    device.wake();
  5.    // fall thru
  6. case ready:
  7.    device.run();
  8.    break;
  9. case bad:
  10.    handle_error();
  11.    break;
  12. }
复制代码
C++17 可以这样写
  1. switch (device.status())
  2. {
  3. case sleep:
  4.    device.wake();
  5.    [[fallthrough]];
  6. case ready:
  7.    device.run();
  8.    break;
  9. case bad:
  10.    handle_error();
  11.    break;
  12. }
复制代码
再之前的代码编译器会告诉你没有break的警告,但是再c++17中使用fallthrough属性就可以消除这个警告了
[[maybe_unused]] 标准属性
可用来消除未使用的函数和变量编译器所发出的警告
example
  1. [[maybe_unused]] bool testUnusedVariable = true;

  2. [[maybe_unused]]
  3. void TestUnusedFunction() {
  4.   
  5. }
复制代码
[[nodiscard]] 标准属性
如果你的某个函数的返回值特别重要,希望使用者不要忽略,可以添加这个属性,再编译的时候如果函数使用者没有使用返回值将会有一个警告产生
example
[[nodiscard]] bool TestNodiscard()
{    return true;}
[[deprecated]] 标准属性
提示允许使用声明有此属性的名称或实体,但因为一些原因不鼓励使用,一般用在即将废弃的函数,但是还有老的用户使用到了这个函数
example
// 再vs中这个会不是警告而是错误.[[deprecated("test deprecated")]] bool TestDeprecated() {    return true;}[[noretrun]] 标准属性
告知函数并没有返回值
example
[[noreturn]] void TestNoreturn() {}string_view
C++17 中特别新增的一个特别好用且重要的特性,string_view相对于string来说就是一个只读的string,string_view的赋值操作的空间成本和时间成本远远胜于string,string_view的赋值特别像一个指针的赋值,一般来说再一下情况下使用string_view会更合适
exampel
  1. // 常量string
  2. const std::string = "hello world";
  3. // string_view 更为合适
  4. const string_view = "hello world";

  5. // 函数参数
  6. void Function1(const std::string& arg1) {

  7. }

  8. // string_view 更为合适
  9. void Function1(string_view arg1) {
复制代码
filesystem
在没有C++17时一直就使用experiment/filesystem,在C++17 filesystem被正式纳入C++标准库中, 由于大多数人对filesystem都比较熟悉了,在这里只是简单的介绍一下
example
  1. std::filesystem::path path("testpath");
  2. if (std::filesystem::exists(path)) {
  3.   // 存在
  4. } else {
  5.   // 不存在
  6. }
复制代码
any
any是一个可用于任何类型单个值的类型安全容器,如果你之前有了解过boost相信对这个any类已经非常熟悉了
example
  1. std::any Int = 69;
  2. std::any Double = 69.123;
  3. std::any String = std::string_view("Hello");

  4. std::cout << Int.type().name() << std::endl;
  5. std::cout << Double.type().name() << std::endl;
  6. std::cout << Double.type().name() << std::endl;

  7. std::vector<std::any> anys = { Int, Double, String };
  8. std::cout << std::any_cast<int>(Int) << std::endl;
  9. std::cout << std::any_cast<double>(Double) << std::endl;
  10. std::cout << std::any_cast<std::string_view>(String) << std::endl;

  11.     // has_value: 是否有值
  12. std::any a = 1;
  13. if (a.has_value()) {
  14.   std::cout << a.type().name() << std::endl;// i
  15. }

  16. // reset:清空容器
  17. a.reset();
  18. if (a.has_value()) {
  19.   std::cout << "no value\n";// no value
  20. }
复制代码
optional
熟悉boost的也应该非常熟悉optional了,它最常用的地方是在你返回值是string或者int等出现错误之后非常隐式的表达的地方,使用std:ptional就可以帮你解决这种问题
example
  1. [[nodiscard]]
  2. std::optional<int> TestOptional() {
  3.     // 之前我们可能需要使用return -1,代表错误,现在使用st::optional就不需要那种太过于隐式的表达
  4.     if (true) {
  5.         return 9999;
  6.     } else {
  7.         return std::nullopt;
  8.     }
  9. }

  10. [[nodiscard]]
  11. std::optional<std::string> TestOptional2() {
  12.     // 之前我们可能需要使用return -1,代表错误,现在使用st::optional就不需要那种太过于隐式的表达
  13.     if (true) {
  14.         return "helloworld";
  15.     } else {
  16.         return std::nullopt;
  17.     }
  18. }

  19. // optional
  20. auto result = TestOptional();
  21. if (result.has_value()) {
  22.   // 有值,代表成功
  23. } else {
  24.   // result没有值代表失败
  25. }

  26. // 这个value_or表示当TestOptional的返回值为nullopt时使用or里面的值
  27. auto ret = TestOptional2().value_or("");
复制代码
variant
variant用来表示一个类型安全的联合体,variant 的一个实例在任意时刻要么保有其一个可选类型之一的值,要么在错误情况下无值。
variant 不容许保有引用、数组,或类型 void, 空variant可以使用std::variant<std::monostate>

下面这个例子是一个实际接口设计时利用std::variant解决不同类型不同参数的接口
  1. // variant
  2. struct SystemProxyConfig {
  3.   bool isService;
  4. };

  5. struct CustomProxyConfig {
  6.   bool isFile;
  7.   std::string pathOrContent;
  8. };
  9. std::variant<SystemProxyConfig, CustomProxyConfig> config;
  10. //config = CustomProxyConfig{ false, "http://192.168.21.161/spiderweb.pac" };
  11. config = SystemProxyConfig{ false };
  12. if (std::get_if<CustomProxyConfig>(&config)) {
  13.   // 类型成功
  14.   CustomProxyConfig customConfig = std::get<CustomProxyConfig>(config);
  15. } else {
  16.   // 类型失败
  17.   SystemProxyConfig systemProxyConfig = std::get<SystemProxyConfig>(config);
  18.   int i = 0;
  19. }
复制代码
execution
execution为C++STL算法库提供了一种算法的执行策略设置,目前支持的策略:
  • sequenced_policy (顺序执行策略)
  • parallel_policy (并行执行策略)
  • parallel_unsequenced_policy (并行及无序执行策略)
  • unsequenced_policy (无序执行策略)
example
  1. std::vector<int> testExecution{ 1, 2, 3, 4, 5,8, 19, 20 ,30,40,50,0,102,40,10,30,20,1000,32,31,34,45};
  2.     auto it1 = std::find(std::execution::seq, testExecution.begin(), testExecution.end(), 5);
  3.     auto it2 = std::find(std::execution::par, testExecution.begin(), testExecution.end(), 5);
  4.     auto it3 = std::find(std::execution::par_unseq, testExecution.begin(), testExecution.end(), 5);
复制代码


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则