• C++17新特性


    C++17新特性

    前言

    前项目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

    std::map<std::string, int> map;
    map["nihao"] = 1;
    map["shijie"] = 2;
    if (auto ret = map.begin(); ret != map.end()) {
      std::cout << ret->first << ": " << ret->second;
    }
    

    Constexpr if

    constexpr if 语句中,条件 的值必须是可按语境转换到 bool 类型的经转换常量表达式。若其值为 true,则舍弃 false分支语句(若存在),否则舍弃 true分支语句。

    通常我们写业务很难用到,在模板元编程中会特别有用

    example

    template <typename T>
    void TestConstexprIf(T value) {
        if constexpr (std::is_integral_v<T>)
            std::cout << "is integral" << std::endl;
        else
            static_assert(false, "T必须是整型");
    }
    

    inline 变量

    inline 变量用来解决一个问题,全局变量再头文件中定义之后多出使用产生符号重定义错误

    错误例子

    // test.h 头文件
    int test = 10;
    
    // test1.cpp 
    void Function1() {
      test = 20;
    }
    
    // test2.cpp
    void Function() {
      test = 30;
    }
    
    
    // 上面的代码编译将会产生重定义错误,c++17之前解决方案是使用extern导出全局变量
    
    // 解决方案
    
    // test.h 头文件
    extern int test;
    
    // test.cpp
    int test = 10;
    

    C++17 之后引入inline变量使其全局变量可以直接再头文件中声明定义

    example

    inline int test = 10;
    

    嵌套命名空间

    example

    namespace test::test2 {
    int i = 0;
    }
    
    std::cout << test::test2::i << std::endl;
    

    属性说明符

    属性为各种由实现定义的语言扩展(例如 GNUIBM 的语言扩展 __attribute__((...)),微软的语言扩展 __declspec() 等)提供了统一化的语法。

    [[fallthrough]] 标准属性

    指示从前一标号直落是有意的,而在发生直落时给出警告的编译器不应诊断它。

    C++17 之前的标准下, 有如下代码

    switch (device.status())
    {
    case sleep:
       device.wake();
       // fall thru
    case ready:
       device.run();
       break;
    case bad:
       handle_error();
       break;
    }
    

    C++17 可以这样写

    switch (device.status())
    {
    case sleep:
       device.wake();
       [[fallthrough]];
    case ready:
       device.run();
       break;
    case bad:
       handle_error();
       break;
    }
    

    再之前的代码编译器会告诉你没有break的警告,但是再c++17中使用fallthrough属性就可以消除这个警告了

    [[maybe_unused]] 标准属性

    可用来消除未使用的函数和变量编译器所发出的警告

    example

    [[maybe_unused]] bool testUnusedVariable = true;
    
    [[maybe_unused]] 
    void TestUnusedFunction() {
      
    }
    

    [[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来说就是一个只读的stringstring_view的赋值操作的空间成本和时间成本远远胜于stringstring_view的赋值特别像一个指针的赋值,一般来说再一下情况下使用string_view会更合适

    exampel

    // 常量string
    const std::string = "hello world";
    // string_view 更为合适
    const string_view = "hello world";
    
    // 函数参数
    void Function1(const std::string& arg1) {
    
    }
    
    // string_view 更为合适
    void Function1(string_view arg1) {
    
    }
    
    

    filesystem

    在没有C++17时一直就使用experiment/filesystem,在C++17 filesystem被正式纳入C++标准库中, 由于大多数人对filesystem都比较熟悉了,在这里只是简单的介绍一下

    example

    std::filesystem::path path("testpath");
    if (std::filesystem::exists(path)) {
      // 存在
    } else {
      // 不存在
    }
    

    any

    any是一个可用于任何类型单个值的类型安全容器,如果你之前有了解过boost相信对这个any类已经非常熟悉了

    example

    std::any Int = 69;
    std::any Double = 69.123;
    std::any String = std::string_view("Hello");
    
    std::cout << Int.type().name() << std::endl;
    std::cout << Double.type().name() << std::endl;
    std::cout << Double.type().name() << std::endl;
    
    std::vector<std::any> anys = { Int, Double, String };
    std::cout << std::any_cast<int>(Int) << std::endl;
    std::cout << std::any_cast<double>(Double) << std::endl;
    std::cout << std::any_cast<std::string_view>(String) << std::endl;
    
        // has_value: 是否有值
    std::any a = 1;
    if (a.has_value()) {
      std::cout << a.type().name() << std::endl;// i
    }
    
    // reset:清空容器
    a.reset();
    if (a.has_value()) {
      std::cout << "no value
    ";// no value
    }
    

    optional

    熟悉boost的也应该非常熟悉optional了,它最常用的地方是在你返回值是string或者int等出现错误之后非常隐式的表达的地方,使用std::optional就可以帮你解决这种问题

    example

    [[nodiscard]]
    std::optional<int> TestOptional() {
        // 之前我们可能需要使用return -1,代表错误,现在使用st::optional就不需要那种太过于隐式的表达
        if (true) {
            return 9999;
        } else {
            return std::nullopt;
        }
    }
    
    [[nodiscard]]
    std::optional<std::string> TestOptional2() {
        // 之前我们可能需要使用return -1,代表错误,现在使用st::optional就不需要那种太过于隐式的表达
        if (true) {
            return "helloworld";
        } else {
            return std::nullopt;
        }
    }
    
    // optional 
    auto result = TestOptional();
    if (result.has_value()) {
      // 有值,代表成功
    } else {
      // result没有值代表失败
    }
    
    // 这个value_or表示当TestOptional的返回值为nullopt时使用or里面的值
    auto ret = TestOptional2().value_or("");
    

    variant

    variant用来表示一个类型安全的联合体,variant 的一个实例在任意时刻要么保有其一个可选类型之一的值,要么在错误情况下无值。
    variant 不容许保有引用、数组,或类型 void, 空variant可以使用std::variant<std::monostate>


    下面这个例子是一个实际接口设计时利用std::variant解决不同类型不同参数的接口
    // variant
    struct SystemProxyConfig {
      bool isService;
    };
    
    struct CustomProxyConfig {
      bool isFile;
      std::string pathOrContent;
    };
    std::variant<SystemProxyConfig, CustomProxyConfig> config;
    //config = CustomProxyConfig{ false, "http://192.168.21.161/spiderweb.pac" };
    config = SystemProxyConfig{ false };
    if (std::get_if<CustomProxyConfig>(&config)) {
      // 类型成功
      CustomProxyConfig customConfig = std::get<CustomProxyConfig>(config);
    } else {
      // 类型失败
      SystemProxyConfig systemProxyConfig = std::get<SystemProxyConfig>(config);
      int i = 0;
    }
    

    execution

    executionC++STL算法库提供了一种算法的执行策略设置,目前支持的策略:

    • sequenced_policy (顺序执行策略)
    • parallel_policy (并行执行策略)
    • parallel_unsequenced_policy (并行及无序执行策略)
    • unsequenced_policy (无序执行策略)

    example

        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};
        auto it1 = std::find(std::execution::seq, testExecution.begin(), testExecution.end(), 5);
        auto it2 = std::find(std::execution::par, testExecution.begin(), testExecution.end(), 5);
        auto it3 = std::find(std::execution::par_unseq, testExecution.begin(), testExecution.end(), 5);
    

    参考链接

    cppreference.com

    fallthrough属性

  • 相关阅读:
    nodejs微服务健康检查方案
    RabbitMQ inequivalent arg 'durable' for exchange 'csExchange' in vhost '/': received
    python 虚拟环境
    gulp-babel使用
    node-schedule定时任务
    微信小程序分包(附微信小程序开发学习手册)
    抖音、腾讯、京东、阿里等大厂性能优化方案总结(含项目实战分析及视频)
    微信小程序绘制二维码(附微信小程序开发学习手册)
    【纯干货分享】拒绝卡顿,揭秘盒马鲜生 APP Android 短视频秒播优化方案
    碰壁五次!我闭关28天啃完这些书,再战拿下腾讯,爱奇艺,小红书,快手等10家大厂!化身offer收割机!
  • 原文地址:https://www.cnblogs.com/gd-luojialin/p/15028180.html
Copyright © 2020-2023  润新知