• C++ 17新特性


    目录

    构造函数模板推导

    Abstract

    Demo

    结构化绑定

    Abstract

    Demo

    if-switch初始化

    Abstract

    内联变量

    Abstract

    折叠表达式

    Abstract

    编译器lambda表达式

    Abstract

    namespace 嵌套

    Abstract

    lambda抓取this副本

    Abstract

    字符串转化

    Abstract

    std::variant

    Abstract

    std::optional

    Abstract

    std::apply

    Abstract

    std::string_view

    Abstract

    as_const

    Abstract

    std::make_from_tuple

    Abstract

    std::shared_mutex读写锁

    Abstract


    构造函数模板推导

    Abstract

    在 C++17 之前,通过向模板类的构造函数传递参数无法推导出类的模板参数类型.一般要通过一个帮助函数来构造一个对象,通过传递给帮助函数的参数推导出参数类型,然后在帮助函数中返回一个构造好的对象。C++17之后,可以直接通过构造函数的参数类型来推导出模板参数类型.

    Demo

    template 
    class my_class {
        T1 t1;
        T2 t2;
        T3 t3;
    public:
        explicit my_class(T1 t1_, T2 t2_, T3 t3_)
        : t1{t1_}, t2{t2_}, t3{t3_}
        {}
    };
    
    //C++ 17 before
    my_class obj{1, 2, 3.0};
    my_class make_my_class(T1 t1, T2 t2, T3 t3)
    {
        return {t1, t2, t3};
    }
    auto my_obj (make_my_class(1, 2, 3.0));
    
    //C++ 17 later
    my_class obj(1, 2, 3.0);
    // (1)
    template 
    struct sum {
        T value;
    
        template 
        sum(Ts&& ... values) : value{(values + ...)} {}
    };
    
    // (2)
    template 
    sum(Ts&& ... ts) -> sum>;

    结构化绑定

    Abstract

    注意结构化绑定不能应用于constexpr

    结构化绑定允许声明多个变量,可以使用数组,结构体 ,pair等中的元素来初始化。

    Demo

    #include 
    #include 
    using namespace std;
     
    int main()
    {
        map m = { {1, "hi"}, {2, "hello"}};
     
        for(auto [index, str] : m)    //将index和str直接绑定到map的内部对象上
        {
        	cout<<"index:"<
    #include 
    using namespace std;
     
    class A{
    public:
    	int d1 = 1;
    	int d2 = 2;
    };
    int main()
    {
        int a[] = {1, 2, 3};
        auto &[m1,m2,m3] =  a;                     //绑定到数组
        cout<

    结构化绑定不止可以绑定pair和tuple,还可以绑定数组和结构体等。

    int array[3] = {1, 2, 3};
    auto [a, b, c] = array;
    cout << a << " " << b << " " << c << endl;
    
    // 注意这里的struct的成员一定要是public的
    struct Point {
       int x;
       int y;
    };
    Point func() {
       return {1, 2};
    }
    const auto [x, y] = func();

    if-switch初始化

    Abstract

    C++17前if语句需要这样写代码:

    int a = GetValue();
    if (a < 101) {
       cout << a;
    }

    C++17之后可以这样:

    // if (init; condition)
    
    if (int a = GetValue()); a < 101) {
       cout << a;
    }
    
    string str = "Hi World";
    if (auto [pos, size] = pair(str.find("Hi"), str.size()); pos != string::npos) {
       std::cout << pos << " Hello, size is " << size;
    }

    内联变量

    Abstract

    C++17前只有内联函数,现在有了内联变量,我们印象中C++类的静态成员变量在头文件中是不能初始化的,但是有了内联变量,就可以达到此目的:

    // header file
    struct A {
       static const int value;  
    };
    inline int const A::value = 10;
    
    // ==========或者========
    struct A {
       inline static const int value = 10;
    }

    折叠表达式

    Abstract

    C++17引入了折叠表达式使可变参数模板编程更方便:

    template 
    auto sum(Ts ... ts) {
       return (ts + ...);
    }
    int a {sum(1, 2, 3, 4, 5)}; // 15
    std::string a{"hello "};
    std::string b{"world"};
    cout << sum(a, b) << endl; // hello world

    编译器lambda表达式

    Abstract

    C++17前lambda表达式只能在运行时使用,C++17引入了constexpr lambda表达式,可以用于在编译期进行计算。

    注意

    constexpr函数有如下限制:

    函数体不能包含汇编语句、goto语句、label、try块、静态变量、线程局部存储、没有初始化的普通变量,不能动态分配内存,不能有new delete等,不能虚函数。

    int main() { // c++17可编译
       constexpr auto lamb = [] (int n) { return n * n; };
       static_assert(lamb(3) == 9, "a");
    }

    namespace 嵌套

    Abstract

    namespace A {
       namespace B {
           namespace C {
               void func();
          }
      }
    }
    
    // c++17,更方便更舒适
    namespace A::B::C {
       void func();)
    }

    lambda抓取this副本

    Abstract

    C++17增加了新特性,捕获*this,不持有this指针,而是持有对象的拷贝,这样生命周期就与对象的生命周期不相关啦。

    struct A {
       int a;
       void func() {
           auto f = [*this] { // 这里
               cout << a << endl;
          };
           f();
      }  
    };
    int main() {
       A a;
       a.func();
       return 0;
    }

    字符串转化

    Abstract

    新增from_chars函数和to_chars函数

    #include 
    
    int main() {
        const std::string str{"123456098"};
        int value = 0;
        const auto res = std::from_chars(str.data(), str.data() + 4, value);
        if (res.ec == std::errc()) {
            cout << value << ", distance " << res.ptr - str.data() << endl;
        } else if (res.ec == std::errc::invalid_argument) {
            cout << "invalid" << endl;
        }
        str = std::string("12.34);
        double val = 0;
        const auto format = std::chars_format::general;
        res = std::from_chars(str.data(), str.data() + str.size(), value, format);
    
        str = std::string("xxxxxxxx");
        const int v = 1234;
        res = std::to_chars(str.data(), str.data() + str.size(), v);
        cout << str << ", filled " << res.ptr - str.data() << " characters \n";
        // 1234xxxx, filled 4 characters
    }

    std::variant

    Abstract

    C++17增加std::variant实现类似union的功能,但却比union更高级,举个例子union里面不能有string这种类型,但std::variant却可以,还可以支持更多复杂类型,如map等

    int main() { // c++17可编译
        std::variant var("hello");
        cout << var.index() << endl;
        var = 123;
        cout << var.index() << endl;
    
        try {
            var = "world";
            std::string str = std::get(var); // 通过类型获取值
            var = 3;
            int i = std::get<0>(var); // 通过index获取对应值
            cout << str << endl;
            cout << i << endl;
        } catch(...) {
            // xxx;
        }
        return 0;
    }

    注意

    一般情况下variant的第一个类型一般要有对应的构造函数,否则编译失败:

    struct A {
        A(int i){}
    };
    int main() {
        std::variant var; // 编译失败
    }

    如何避免这种情况呢,可以使用std::monostate来打个桩,模拟一个空状态。

    std::variant var; // 可以编译成功

    std::optional

    Abstract

    我们有时候可能会有需求,让函数返回一个对象,如下:

    struct A {};
    A func() {
        if (flag) return A();
        else {
            // 异常情况下,怎么返回异常值呢,想返回个空呢
        }
    }

    有一种办法是返回对象指针,异常情况下就可以返回nullptr啦,但是这就涉及到了内存管理,也许你会使用智能指针,但这里其实有更方便的办法就是std::optional。

    std::optional StoI(const std::string &s) {
        try {
            return std::stoi(s);
        } catch(...) {
            return std::nullopt;
        }
    }
    
    void func() {
        std::string s{"123"};
        std::optional o = StoI(s);
        if (o) {
            cout << *o << endl;
        } else {
            cout << "error" << endl;
        }
    }

    std::apply

    Abstract

    使用std::apply可以将tuple展开作为函数的参数传入,见代码:

    int add(int first, int second) { return first + second; }
    
    auto add_lambda = [](auto first, auto second) { return first + second; };
    
    int main() {
        std::cout << std::apply(add, std::pair(1, 2)) << '\n';
        std::cout << add(std::pair(1, 2)) << "\n"; // error
        std::cout << std::apply(add_lambda, std::tuple(2.0f, 3.0f)) << '\n';
    }

    std::string_view

    Abstract

    通常我们传递一个string时会触发对象的拷贝操作,大字符串的拷贝赋值操作会触发堆内存分配,很影响运行效率,有了string_view就可以避免拷贝操作,平时传递过程中传递string_view即可

    void func(std::string_view stv) { cout << stv << endl; }
    
    int main(void) {
        std::string str = "Hello World";
        std::cout << str << std::endl;
    
        std::string_view stv(str.c_str(), str.size());
        cout << stv << endl;
        func(stv);
        return 0;
    }

    as_const

    Abstract

    C++17使用as_const可以将左值转成const类型

    std::string str = "str";
    const std::string& constStr = std::as_const(str);

    std::make_from_tuple

    Abstract

    使用make_from_tuple可以将tuple展开作为构造函数参数

    struct Foo {
        Foo(int first, float second, int third) {
            std::cout << first << ", " << second << ", " << third << "\n";
        }
    };
    int main() {
       auto tuple = std::make_tuple(42, 3.14f, 0);
       std::make_from_tuple(std::move(tuple));
    }

    std::shared_mutex读写锁

    Abstract

    读写锁把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。C++17开始,标准库提供了shared_mutex类(在这之前,可以使用boost的shared_mutex类或系统相关api)。和其他便于独占访问的互斥类型不同,shared_mutex 拥有两个访问级别:

    • 共享:多个线程能共享同一互斥的所有权(如配合shared_lock);
    • 独占:仅有一个线程能占有互斥(如配合lock_guard、unique_lock)。

    C++14通过shared_timed_mutex提供了读写锁,而C++17通过shared_mutex提供了读写锁。

    #include 
    #include 
    #include 
    #include 
    using namespace std;
     
    shared_mutex sLock;
    int g_data = 10;
     
    unsigned long getTime()
    {
    	return chrono::system_clock::now().time_since_epoch().count()/chrono::system_clock::period::den;
    }
     
    void read_shared_data(int id)
    {
    	cout << getTime() << " thread "<< id << " begin" << endl;
    	shared_lock slk(sLock);
    	cout << getTime() << " thread " << id << " read data=" << g_data << endl;
    }
     
    int main()
    {
    	shared_lock slk(sLock);              //读锁加锁
    	cout << getTime() << " shared_lock locked"< ulk(sLock);              //写锁加锁
    	cout << getTime() << " unique_lock locked"<

  • 相关阅读:
    SpringCloud——服务注册——Eureka
    系统安装技能测试
    Linux电话本的编写-shell脚本编写
    怎样来实现流量削峰方案
    HTML前端
    PCL可视化只有顶点彩色信息的OBJ文件
    3.1 OrCAD中怎么创建新的原理图工程文件?OrCAD中原理图的设计纸张大小应该怎么设置?
    Linux安装Rabbitmq
    vue项目嵌套(vue2嵌套vue3)
    车辆车型识别系统python+TensorFlow+Django网页界面+算法模型
  • 原文地址:https://blog.csdn.net/qq_32378713/article/details/126360364