C++11-C++17新特性介绍

C++(17):any C++17增加了any类型,通过any声明的变量可以保存任意类型的值: 可见通过any可以定义类似于动态语言一样的动态绑定的变量。通过成员函数type()确定其运行时的实际类型: 可以通过reset()等方式将其值清空,清空后type()返回void,并可以通过has_value进行检测: 如果初始化时的字面量值无法确切的表明其类型,可以通过指定类型或in_place_type进行赋值: any定义的变量不能随便的访问,必须通过any_cast将其转换为合适的类型后,才能读取或操作其值,这一 阅读详情
AI摘要

本文聚焦于C++11 - C++17的新特性,涵盖列表初始化、auto与decltype、拖尾返回类型等内容。介绍了各特性的基本语法、使用限制和应用场景,如右值引用用于移动语义和完美转发,还剖析了std::move()与std::forward()源码,为C++开发者提供参考。


前言

  • 本篇只包含c++11- c++17的新特性, 一些c++基本规定不再赘述
  • 前文 c++规定 记录了c++基本规定
  • 对于这些新特性,笔者一边学习一边做笔记,所以内容有待完善
  • 笔者使用的是 Clion+MinGW 开发环境

提示:以下是本篇文章正文内容,下面案例可供参考

一、{列表初始化}

  • int a=8 等同于 int a{8}

  • int b{} 初始化为0

  • #include <iostream>
    
    int main(int argc, char const *argv[])
    {
        struct Demo
        {
            int a_;
            int b_;
            Demo(std::initializer_list<int> list)
            {
                std::cout << "Demo(std::initializer_list<int> list)" << std::endl;
            }
        };
        struct Demo1
        {
            int a_;
            int b_;
            Demo1(int a, int b)
            {
                a_ = a;
                b_ = b;
                std::cout << "Demo1(int a, int b)" << std::endl;
            }
        };
        struct Demo2
        {
            int a_;
            int b_;
            Demo2(int a, int b)
            {
                a_ = a;
                b_ = b;
                std::cout << "Demo2(int a, int b)" << std::endl;
            }
            Demo2(std::initializer_list<int> list)
            {
                std::cout << "Demo2(std::initializer_list<int> list)" << std::endl;
            }
        };
        // 非聚合体首先考虑 initializer_list 构造函数
        Demo d{1, 2}; //花括号里的数据的类型必须一致
        Demo1 d1({1, 2}); //等同于d1(1,2) d1{1,2} 编译时未发现initializer_list<int>形参 就把括号内的值一一匹配赋给 Demo2(int a, int b)中的a和b了
        Demo2 d2{1, 2};   //编译器会自动生成一个匿名的initializer_list类型的对象 把这条语句转换为 Demo d2({1,2})
        // Demo2 d2(1, 2);//Demo2(int a, int b) 园括号就是直接指明要调用括号内的就是参数
        return 0;
        /*输出 :
        Demo(std::initializer_list<int> list)
        Demo1(int a, int b)
        Demo2(std::initializer_list<int> list) 
          需要注意的是,initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。
       并且,拷贝或赋值一个initializer_list对象不会拷贝列表中的元素,其实只是引用而已,原始列表和副本共享元素。
       和使用vector一样,我们也可以使用迭代器访问initializer_list里的元素
       如果想向initializer_list形参中传递一个值的序列,则必须把序列放在一对花括号内:*/
    }
    
  • 与数组的关系

    #include <iostream>
    using namespace std;
    template<class T,size_t N>
    T average(const T (&array)[N]) //因为初始化列表是常量 所以要用const 类型接收
    {
        T sum{};
        for(size_t i{};i<N;++i)
            sum+=array[i];
        return sum/N ;
    }
    int main()
    {
        cout<<average({1.0,2.0,3.0,4.0,5.0})<<endl; //T=double N 为 5
        return 0;
    }
    
  • 与new

    int* a = new int { 123 };
    double b = double { 12.12 };
    int* arr = new int[3] { 1, 2, 3 };
    
  • 推荐以后多使用{}来初始化 更加 type-safe

二、auto 与 decltype 与 decltype(auto)

  • auto

    • C++11 赋予 auto 关键字新的含义,使用它来做自动类型推导。也就是说,使用了 auto 关键字以后,编译器会在编译期间自动推导出变量的类型,这样我们就不用手动指明变量的数据类型了。
    • auto基本使用语法:
      auto name = value;
    • 和某些具体类型混合使用
      int  x = 0;
      auto *p1 = &x;   //p1 为 int *,auto 推导为 int
      auto  p2 = &x;   //p2 为 int*,auto 推导为 int*
      auto &r1  = x;   //r1 为 int&,auto 推导为 int
      auto r2 = r1;    //r2 为  int,auto 推导为 int
      //---------------------------------------------------------------
      const int a = 0;
      auto b = a;     //a 为 const int, auto 被推导为 int(const 属性被抛弃)
      auto &c = a;    //a 为 const int, auto 被推导为 const int(const 属性被保留)c 为 const int&类型
      //-------------------------------------------------------------------
      int n = 10;
      int && r2 = std::move(n);
      int &r3 = n;    //r3是一个int &类型
      auto r4 = r3;   //auto 推导为 int  抛弃了左值引用
      auto r5 = r2;   //auto 推导为 int  抛弃了右值引用
      
    • auto不会推断为一个引用类型,而总是推断为一个值类型,这意味着即使将一个引用赋值给auto,值也会被复制
      但可以使用auto& 或const auto&
    • 对 cv 限定符的处理
      • 「cv 限定符」是 const 和 volatile 关键字的统称:
      • volatile它用来表示数据是可变的、易变的,目的是不让 CPU 将数据缓存到寄存器,而是直接存取原始内存地址
      • 如果表达式的类型不是指针或者引用,auto 会把 cv 限定符直接抛弃
      • 如果表达式的类型是指针或者引用,auto 将保留 cv 限定符。
    • auto的使用限制:
      • auto 不能在函数的参数中使用
        >.warning: use of 'auto' in parameter declaration only available with '-std=c++20' or '-fconcepts'
        >. 我开玩笑的,其实能用 但 auto 和函数参数默认值不能用在同一个参数上(该函数参数默认性将失效)
      • auto 不能作用于类的非静态成员变量
      • auto 关键字不能定义数组
      • auto 不能作用于模板的类型参数
    • 从c++ 17起,可以使用auto来声明一个非类型模板参数。
  • decltype

    • 在某些特殊情况下auto用起来非常不方便,甚至压根无法使用,所以 decltype 关键字也被引入到 C++11 中。

    • decltype基本使用语法:
      decltype(exp) varname = value;
      其中,varname 表示变量名,value 表示赋给变量的值,exp 表示一个表达式。

    • auto 要求变量必须初始化,而 decltype 不要求:
      decltype(exp) varname;

    • exp 是一个普通的表达式,它可以是任意复杂的形式,但要保证 exp 的结果是有类型的,不能是 void;

    • 如果 exp 是一个左值 或者被括号()包围:decltype((exp)),那么 返回类型就是 exp 的引用;

    • exp为函数调用表达式时需要带上括号和参数, 这仅仅是形式, 并不会真的去执行函数代码。
      decltype(func(100)) 返回的类型就和函数返回值的类型一致

    • 不同于auto, decltype 会保留 cv 限定符 , 以及引用

    • 和某些具体类型混合使用

      int  x = 1;
      int &&u= 5;
      decltype(u)& y=x; //引用折叠 y为 int& 类型
      
  • decltype(auto)

    • 和auto用法一样 不同处在于 会保留 cv 限定符 , 以及引用 且不能和其他类型混合使用
    • 其他用法

      int a=5;
      decltype(auto) b=(a); //b推导为 int& 类型

三、拖尾返回类型 (trailing-return-type)

  • 函数的返回类型推断是在c++14中引入的,那么之前如何解决呢?

    template <typename T, typename U>
    auto add(T t, U u) -> decltype(t + u)
    {
    return t + u;
    }

  • 因为在decltype()中重复函数体内的表达式很枯燥,所以c++14引入了decltype(auto)语法:
    >.效果同上 等于是上面的简写形式

    template <typename T, typename U>
    decltype(auto) add(T t, U u)
    {
    return t + u;
    }

  • 单纯只有auto

    template <typename T, typename U>
    auto add(T t, U u)
    {
    return t + u;
    }

  • 使用拖尾decltype() 或decltype(auto),与单纯只有auto的返回类型推断并不等效. 前面介绍了auto会抛弃初始化值类型中的引用
    >.这意味着纯auto返回类型推断有时候会不必要的复制值 可以试试返回 const auto&
  • 参考 https://c.biancheng.net/view/3727.html

四、使用 using 定义类型别名

  • using 覆盖了 typedef 的全部功能

    // 重定义unsigned int
    typedef unsigned int uint_t;
    using uint_t = unsigned int;
    // 重定义std::map
    typedef std::map<std::string, int> map_int_t;
    using map_int_t = std::map<std::string, int>;
    //定义函数类型
    typedef void (*func_t)(int, int);
    using func_t = void (*)(int, int);
    
  • 和模板的结合使用

    template <typename Val>
    using str_map_t = map<string, Val>;
    // ...
    str_map_t<int> map1;
    
  • 别名模板(alias template)

    template <typename T>
    using func_t = void (*)(T, T);
    // 使用 func_t 模板
    func_t<int> xx_2;
    

    using 语法和 typedef 一样,并不会创造新的类型 只是原类型的别名
    func_t 定义的 xx_2 并不是一个由类模板实例化后的类,而是 void(*)(int, int) 的别名。
    从这里可以看出模板非常灵活好用

五、模板

六、lambda 函数

  • 基本语法:

    [捕获] (形参列表) mutable noexcept -> 后置返回类型 { 函数体 }

  • 上面mutable noexcept 是可选的 只是表明如果需要用到应该写在哪儿
  • 其中后置返回类型可以省略,编译器会自动推导出 lambda 的返回类型,形参列表也是可选的
    因此最简单的 lambda 定义为 []{}
  • [捕获]用法:
    [捕获]的定义方式
    捕获 功能
    [] 空方括号表示当前 lambda 匿名函数中不导入任何外部变量(全局变量除外);
    [=] 只有一个 = 等号,表示以值传递的方式导入所有外部变量(全局变量除外);
    [&] 只有一个 & 符号,表示以引用传递的方式导入所有外部变量;
    [val1,val2,...] 表示以值传递的方式导入 val1、val2 等指定的外部变量,同时多个变量之间没有先后次序;
    [&val1,&val2,...] 表示以引用传递的方式导入 val1、val2等指定的外部变量,多个变量之间没有前后次序;
    [val,&val2,...] 以上 2 种方式还可以混合使用,变量之间没有前后次序。
    [=,&val1,...] 表示除 val1 以引用传递的方式导入外,其它外部变量都以值传递的方式导入。
    [this] 表示以值传递的方式导入当前的 this 指针,这样就可以无限制直接访问 this 的成员
  • 默认情况下,对于以值传递方式捕获的外部变量,不允许在 lambda 表达式内部修改它们的值
    >.(全局变量除外)
    如果想修改它们,定义时就必须使用 mutable 关键字: [=] mutable {…}
    >.也说了是值传递,所以只能修改的是与外部变量同名的副本变量
  • 从c++14开始 lambda 的形参支持 auto 和 默认实参值
    >.注意:前面讲了auto和函数参数默认值不能用在同一个参数上
  • 考虑:constexpr auto add = [] (auto a,auto b) {return a+b};
    add 是一个值,那么它所属的类型到底是什么呢? 其实 lambda 表达式的结果是一个函数对象
    该函数对象的正式名称是 “lambda闭包”, 但是很多人也称之为 “lambda函数” 或 “lambda”
  • 用法示例:
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int main()
    {
        //display 即为 lambda 匿名函数的函数名
        auto display = [](int a,int b) -> void{cout << a << " " << b;};
        //调用 lambda 函数
        display(10,20);
        
        int num[4] = {4, 2, 3, 1};
        //对 a 数组中的元素进行排序
        sort(num, num+4, [=](int x, int y) -> bool{ return x < y; } );
        for(int n : num){
            cout << n << " ";
        }
        return 0;
    }
    

七、POD and union

  • POD: Plain Old Data 简洁旧数据

  • POD 类型一般具有以下几种特征(包括 class、union 和 struct等)

    1. 没有用户自定义的构造函数、析构函数、拷贝构造函数和移动构造函数。
    2. 不包含虚函数和虚基类。
    3. 非静态成员声明为 public。
  • C++11 允许联合体有静态成员,构造函数,析构函数,重载运算符…像类一样

  • 如果联合体内有一个非 POD 的成员,那么这个联合体的默认构造函数将被编译器删除;其他的特殊成员函数,例如默认拷贝构造函数、拷贝赋值操作符以及析构函数等,也将被删除。

    #include <string>
    using namespace std;
    union U {
       string s;
       int n;
    };
    int main() {
    	U u;   // 构造失败,因为 U 的构造函数被删除
      return 0;
    }
    
  • 解决上面问题的一般需要用到 placement new

    #include <string>
    using namespace std;
    union U {
        string s;
        int n;
    public:
        U() { new(&s) string; }
        ~U() { s.~string(); }
    };
    int main() {
        U u;
        return 0;
    }
    
  • 构造时,采用 placement new 将 s 构造在其地址 &s 上,这里 placement new 的唯一作用只是调用了一下 string 类的构造函数。注意,在析构时还需要调用 string 类的析构函数。

  • placement new 是什么?

    placement new 是 new 关键字的一种进阶用法,既可以在栈(stack)上生成对象,也可以在堆(heap)上生成对象。相对应地,我们把常见的 new 的用法称为 operator new,它只能在 heap 上生成对象。

    • placement new 的语法格式如下:
      new(address) ClassConstruct(…)
    • address 表示已有内存的地址,该内存可以在栈上,也可以在堆上;ClassConstruct(…) 表示调用类的构造函数,如果构造函数没有参数,也可以省略括号。
    • placement new 利用已经申请好的内存来生成对象,它不再为对象分配新的内存,而是将对象数据放在 address 指定的内存中。在本例中,placement new 使用的是 s 的内存空间。

八、range for

  • 基本用法:
    for(T& 变量名: arr){循环体} 变量名表示是arr中每个元素的引用
    for(T 变量名: arr){循环体} 变量名表示是arr中每个元素的复制
  • 示例:

    for (char ch : “HelloC++11-C++17”)
    cout << ch;

  • 只能用于同一类型构成的序列对象
  • range for 不能用于指针

九、右值引用 (rvalue reference)

  • 前文 c++规定 介绍的引用称为 左值引用 (lvalue reference)

  • 右值引用常用于 移动语义 和 完美转发

  • 通常情况下,判断某个表达式是左值还是右值,最常用的有以下 2 种方法。

    • 可位于赋值号(=)左侧的表达式就是左值;反之,只能位于赋值号右侧的表达式就是右值。
    • 有名称的、可以获取到存储地址的表达式即为左值;反之则是右值。
  • 基本用法:
    int && a = 10;

  • a是一个右值引用类型, 但它自己是一个左值,可对 a 取地址

  • 右值引用不能用左值初始化

  • 右值引用可以对右值进行修改

  • 左值(lvalue): 左值 lvalue 是有标识符、可以取地址的表达式

  • 纯右值(prvalue): 纯右值 prvalue 是没有标识符、不可以取地址的表达式

  • 将亡值(xvalue): 表达式static_cast<int&&> (value)的结果可以被右值引用绑定,且具备左值的运行时多态性质,对于这种既有左值的特征,同时又能初始化右值引用的情况, 在c++11中将其归为将亡值
    或长这样:

    int&& f(){
     return 3;
    }
    int main()
    {
     f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.
     return 0;
    }
    

    或长这样:

    struct As
    {
        int i;
    };
    As&& f(){
        return As();
    }
    int main()
    {
        f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.
        return 0;
    }
    

    在这里插入图片描述

  • 如果一个 prvalue 被绑定到一个引用上,它的生命周期则会延长到跟这个引用变量一样长。
    >.这条生命期延长规则只对 prvalue 有效,而对 xvalue 无效。如果由于某种原因,prvalue 在绑定到引用以前已经变成了 xvalue,那生命期就不会延长。

  • T&& Doesn’t Always Mean “Rvalue Reference”
    -------by Scott Meyers

  • int && var1 = someWidget; // here, “&&” means rvalue reference
    //
    auto&& var2 = var1; // here, “&&” does not mean rvalue reference
    //
    template<typename T>
    void f(std::vector&& param); // here, “&&” means rvalue reference
    //
    template<typename T>
    void f(T&& param); // here, “&&”does not mean rvalue reference

十、万能引用与引用折叠(universal reference and reference collapsing)

  • 万能引用
    • If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.
      如果一个变量或者参数被声明为T&&,其中T是被推导的类型,那这个变量或者参数就是一个universal reference。

    • "T需要是一个被推导类型"这个要求限制了universal references的出现范围。
    • 在实践当中,几乎所有的universal references都是函数模板的参数。因为auto声明的变量的类型推导规则本质上和模板是一样的,所以使用auto的时候你也可能得到一个universal references。
    • 和所有的引用一样,你必须对universal references进行初始化,而且正是universal reference的initializer决定了它到底代表的是lvalue reference 还是 rvalue reference:
      • 如果用来初始化universal reference的表达式是一个左值,那么universal reference就变成lvalue reference。
      • 如果用来初始化universal reference的表达式是一个右值,那么universal reference就变成rvalue reference。
    • template<typename T>
      void f(T&& param);
      int a;
      f(a); // 传入左值,那么上述的T&& 就是lvalue reference,(int &)也就是左值引用绑定到了左值
      f(1); // 传入右值,那么上述的T&& 就是rvalue reference,(int &&)也就是右值引用绑定到了右值

    • Universal references只以 T&& 的形式出现!即便是仅仅加一个const限定符都会使得“&&”不再被解释为universal reference:

      template<typename T>
      void f(const T&& param); // “&&” means rvalue reference

    • auto中的universal reference

      int &&a=5;
      auto&& b=a; // a是一个lvalue , auto&& 转化为 int & ; lvalue reference
      auto&& c=std::move(a);// std::move(a)是一个rvalue , auto&& 转化为 int &&; rvalue reference

    • 如果一个表达式的结果是左值, 那么就说这个表达式有左值性(lvalueness)
      如果一个表达式的结果是右值, 那么就说这个表达式有右值性(rvalueness)
    • 值类别(value category)和值类型(value type)
      • value category 指的是上面这些左值、右值相关的概念
      • 因为表达式的 lvalueness 或 rvalueness 独立于它的类型,我们就可以有一个 lvalue,但它的类型却是 rvalue reference,也可以有一个 rvalue reference 类型的 rvalue :

        int x = 5;
        decltype(auto) z = std::move(x); // z推导为 int&& , std::move(x)是一个rvalue reference 类型的 rvalue :

  • 引用折叠
    • template<typename T>
      void f(T&& param);
      int x;
      f(10); // invoke f on rvalue
      f(x); // invoke f on lvalue

    • 当用rvalue 10调用 f 的时候, T被推导为 int,实例化的 f 看起来像这样: 这没什么问题

      void f(int&& param); // f instantiated from rvalue

    • 但当我们用lvalue x 来调用 f 的时候,T 被推导为 int&,而实例化的 f 就包含了一个引用的引用:

      void f(int& && param); // initial instantiation of f with lvalue

      • 为了避免编译器对这个代码报错,C++11引入了一个叫做“引用折叠”(reference collapsing)的规则来处理某些像模板实例化这种情况下带来的"引用的引用"的问题。上面折叠为 void f(int& param);
    • 引用折叠只有两条规则:
      • 一个 rvalue reference to an rvalue reference 会变成 (“折叠为”) 一个 rvalue reference.
      • 其他种类的"引用的引用" 会折叠为 lvalue reference.
    • 万能引用 T&& 中 T 最终变成了什么?
      #include <iostream>
      using namespace std;
      template<typename T>
      void f(T&& param){
          decltype(auto) temp{0};
          T T_type = temp;  
      }
      int main() {
          int x;
          int &a=x;
          f(10); // invoke f on rvalue//T_type 为 T 类型 ,  param 为 T&& 类型
          f(x); // invoke f on lvalue //T_type 为 T& 类型  , param 折叠后为 T& 类型
          f(a); // invoke f on lvalue//T_type 为 T& 类型  param 折叠后为 T& 类型
      }
      
      表面上总结:(前面说过模板参数 T 本身的类型推断结果和 auto 类似,不会保留原类型中的引用)
      如果万能引用 T&& 被初始化为 rvalue reference 那么T 最终为 T
      如果万能引用 T&& 被初始化为 lvalue reference 那么T 最终为 T&

十一、移动语义 与 引用限定符

  • 移动语义
    • 简单的理解,移动语义指的就是将其他对象(通常是临时对象)拥有的内存资源“移为已用”。
    • 在使用临时对象初始化新对象时,我们可以将其包含的指针成员指向的内存资源直接移给新对象所有,无需再新copy一份,这大大提高了初始化的执行效率。
    • 示例 :
      #include <iostream>
      using namespace std;
      class demo{
      public:
          demo():num(new int(0)){
              cout<<"construct!"<<endl;
          }
          demo(const demo &d):num(new int(*d.num)){
              cout<<"copy construct!"<<endl;
          }
          //添加移动构造函数
          demo(demo &&d):num(d.num){
              d.num = NULL;
              cout<<"move construct!"<<endl;
          }
          ~demo(){
              cout<<"class destruct!"<<endl;
          }
      private:
          int *num;
      };
      demo get_demo(){
          return demo();
      }
      int main(){
          demo a = get_demo();
          return 0;
      }
      
    • 在 gcc 下添加-fno-elide-constructors 编译标志 执行结果为:

      construct!
      move construct!
      class destruct!
      move construct!
      class destruct!
      class destruct!

    • 当类中同时包含拷贝构造函数和移动构造函数时,如果使用临时对象初始化当前类的对象,编译器会优先调用移动构造函数来完成此操作。只有当类中没有合适的移动构造函数时,编译器才会退而求其次,调用拷贝构造函数。
    • C++11只是提供了能构成移动语义的条件 &&, 具体要怎么实现你的移动语义是按你的项目来定的(是你自己的事)

  • 引用限定符
    • 某些场景中,我们可能需要限制调用成员函数的对象的类别(左值还是右值),为此 C++11 新添加了引用限定符。所谓引用限定符,就是在成员函数的后面添加 “&” 或者 “&&”
    #include <iostream>
    using namespace std;
    class demo {
    public:
        demo(int num):num(num){}
        int get_num()&&{
            return this->num;
        }
    private:
        int num;
    };
    int main() {
        demo a(10);
        //cout << a.get_num() << endl;      // 错误
        cout << move(a).get_num() << endl;  // 正确
        return 0;
    }
    

十二、完美转发

  • 指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。所谓完美,即不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。
  • 举个例子:
    #include <iostream>
    using namespace std;
    //重载被调用函数,查看完美转发的效果
    void otherdef(int & t) {
        cout << "lvalue\n";
    }
    void otherdef(int && t) {
        cout << "rvalue\n";
    }
    //实现完美转发的函数模板
    template <typename T>
    void function(T &&t) {
        otherdef(t);     //t永远是个左值
        otherdef(std::forward<T>(t)); //本例的重点 从类型中保留值的rvalueness和lvaluess
        cout<<"============================"<<endl;
    }
    int main()
    {
        function(5);
        int  x = 1;
        function(x);
        function(move(x));//move(x) 内部调用了 static_cast<int&&>(x)
        int && a = 5;
        function(a);
        return 0;
    }
    
    输出:

    lvalue
    rvalue
    ============================
    lvalue
    lvalue
    ============================
    lvalue
    rvalue
    ============================
    lvalue
    lvalue
    ============================

  • 不难发现,本质问题在于,左值右值在函数调用时,都转化成了左值,使得函数转调用时无法判断左值和右值。
  • std::forward + 万能引用 + 引用折叠 三者结合才能实现完美转发

十三、std::move()与std::forward()源码剖析

  • 先看看 std::remove_reference 是如何工作的

    #include <iostream>
    using namespace std;
    template<typename _Tp>
    struct my_remove_reference
    {
        typedef _Tp   type;
        my_remove_reference(){cout<<"_Tp"<<endl;}
    };
    
    
    // 特化版本
    template<typename _Tp>
    struct my_remove_reference<_Tp&>
    {
        typedef _Tp   type;
        my_remove_reference(){cout<<"_Tp&"<<endl;}
    };
    
    template<typename _Tp>
    struct my_remove_reference<_Tp&&>
    {
        typedef _Tp   type;
        my_remove_reference(){cout<<"_Tp&&"<<endl;}
    };
    
    int main()
    {
    	my_remove_reference<int&&>();
    	my_remove_reference<int>();
    	my_remove_reference<int&>();
        return 0;
    }
    

    输出:

    _Tp&&
    _Tp
    _Tp&

  • std::move

    template<typename _Tp>
    constexpr typename std::remove_reference<_Tp>::type&&
    move(_Tp&& __t) noexcept
    { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
    
  • std::forward

    • 传入左值时

      template<typename _Tp>
      constexpr _Tp&&
      forward(typename std::remove_reference<_Tp>::type& __t) noexcept
      { return static_cast<_Tp&&>(__t); }
      
    • 传入右值时

      template<typename _Tp>
      constexpr _Tp&&
      forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
      {
       	 static_assert(!std::is_lvalue_reference<_Tp>::value,
      	  "std::forward must not be used to convert an rvalue to an lvalue");
      	  return static_cast<_Tp&&>(__t);
      }
      
    • 形参包含引用时, 需要显式指明 类型和值

    • 传入实参后内部出现了很多万能引用和引用折叠, 总起来说forward做了什么?

      • 对于传入的是个lvalue
        • 如果是lvalue reference类型 则返回他本身类型的 lvalue
        • 如果是rvalue reference类型 则返回他本身类型的 rvalue
        • 如果是不含引用 类型 返回(他本身类型+&&(rvalue reference) )类型的 rvalue
          逆推: 万能引用下 T 变成不含引用的情况, 那么传入的参数必定是个右值. 也就是说 forward 只在万能引用领域里才像那么回事
      • 对于传入的是个rvalue
        • 返回他本身类型的 rvalue

十四、默认构造与禁止构造赋值关键字 与 委托构造函数

  • 默认构造函数

    • 一但自定义了任何构造函数, 编译器不再隐式定义默认的无参构造函数
    • 如果仍然想让对像可被默认构造, 可以使用 default 关键字,不能有参数

      class Box{
      Box() = default;
      };

    • 如果编译器在派生类中提供了无参构造函数,那么其基类中必须有非私有的无参构造函数
  • 禁止构造与赋值
    有时候,你可能想要禁止编译器生成默认的copy构造函数或赋值运算符,可以通过 delete 关键字显式的进行说明:

    class B{
    public:
          B(int){ };
          B(double) = delete;
          B& operator= (const B&) = delete;
          B(const B&) = delete;
    };
    int main() {
          B a(1);
          B a1(3.12); //error 
          B a2(a);  //error 
          a2 = a1; //error 
    }
    

    不会禁止派生类的构造函数, 只是禁止它自己的

  • 委托构造函数

    class B{
    public:
        double length {1.0};
        double width {1.0};
        double height {1.0};
        B(double a,double b,double c):length{a},width{b},height{c} {};
        B(double side):B(side,side,side) {};  //委托构造函数
    };
    

十五、mutable, inline, noexcept, constexpr, if constexpr

  • mutable
    mutable 关键字指出, 即使是 const 对象, 其被 mutable 修饰的成员变量仍可以被修改.
    任何成员函数(包括 const 和非 const 成员函数)总是可以修改用 mutable 声明的成员变量
  • inline
    c++17起开始支持内联变量 使得类的静态成员的声明和定义统一在一起
    (自身类型的静态成员变量必须在类外初始化)
  • noexcept
    • 通过在函数头结尾处追加 noexcept 限定符,可指定该函数不会抛出异常 效果同 throw()
      示例:

      void f() noexcept { //…}

    • 注意,这并不意味在函数体内不能抛出异常, 只是说没有异常能够离开函数(不会传播到调用处)
    • 从c++11开始, 析构函数基本上被编译器隐式声明为 noexcept
      原则上,同过显式添加 noexcept(false) 可以定义能够抛出异常的析构函数 但一般不会这么做
  • constexpr
    constexpr 关键字的功能是使指定的常量表达式获得在程序编译阶段计算出结果的能力,而不必等到程序运行阶段。
    以前, 初始式是常量表达式的 const 对象称为 编译时常量 否则称为 运行时常量
    #include <iostream>
    #include <array>
    using namespace std;
    void dis_1(const int x){
        //错误,x是运行时常量 但不属于一个常量表达式 所以叫只读属性的变量比较合适
        array <int,x> myarr{1,2,3,4,5};
        cout << myarr[1] << endl;
    }
    void dis_2(){
        const int x = 5;    //编译时常量 属于常量表达式
        array <int,x> myarr{1,2,3,4,5};
        cout << myarr[1] << endl;
    }
    int main()
    {
        dis_1(5);
        dis_2();
    }
    
    • 这是因为,dis_1() 函数中的“const int x”只是想强调 x 是一个只读的变量,其本质仍为变量,无法用来初始化 array 容器;而 dis_2() 函数中的“const int x”,表明 x 是一个只读变量的同时,x 还是一个值为 5 的常量,所以可以用来初始化 array 容器。
    • C++ 11标准中,为了解决 const 关键字的双重语义问题,保留了 const 表示“只读”的语义,而将“常量”的语义划分给了新添加的 constexpr 关键字。因此 C++11 标准中,建议将 const 和 constexpr 的功能区分开,即凡是表达“只读”语义的场景都使用 const,表达“常量”语义的场景都使用 constexpr。
    • const 用于为修饰的变量添加“只读”属性;而 constexpr 关键字则用于指明其后是一个常量(或者常量表达式),编译器在编译程序时可以顺带将其结果计算出来,而无需等到程序运行阶段
    • C++ 11 标准中,constexpr 可用于修饰普通变量、函数(包括模板函数)以及类的构造函数。
    • 当常量表达式中包含浮点数时,考虑到程序编译和运行所在的系统环境可能不同,常量表达式在编译阶段和运行阶段计算出的结果精度很可能会受到影响,因此 C++11 标准规定,浮点常量表达式在编译阶段计算的精度要至少等于(或者高于)运行阶段计算出的精度。
    • 注意,获得在编译阶段计算出结果的能力,并不代表 constexpr 修饰的表达式一定会在程序编译阶段被计算出结果,具体的计算时机还是编译器说了算。
    • 使用细节请看 C++中的const, constexpr, consteval, constinit 汇总
  • if constexpr
    if constexpr 是C++17引入的特性, 它会在编译期间对布尔常量表达式评估, 并生成对应分支的代码
    基本语法与普通 if 类似:

    if constexpr (布尔常量表达式) {
    //…分支code
    } else if constexpr (布尔常量表达式) {
    //…分支code
    } else {
    //…分支code
    }

十六、重载继承函数 和 用override,final管理虚函数

  • 重载继承函数
    • 通常继承时,产生名字遮蔽的函数,派生类中需要用上域解析符基类名::产生名字遮蔽的函数才能调用基类的, 默认是不构成重载的.
    • 而用上using 就可以在派生类中重载该产生名字遮蔽的函数
      • 如果在public作用域下还能让派生类生成的对象调用该重载
    #include <iostream>
    #include <array>
    using namespace std;
    
    class A{
    public:
        void f(){cout<<"A f"<<endl;}
        void f1(char){cout<<"A f1"<<endl;}
        void f3(int){cout<<"A f3"<<endl;}
    private:
    
    };
    class B:public A{
    public:
        using A::f;
        using A::f1;
        //原型同基类中的 f() 相同
        void f(){ cout<<"B f"<<endl;}
        //原型同基类中的 f1(char) 不同 构成重载
        void f1(){
            f();
            cout<<"B f1"<<endl;
        }
    private:
    
    };
    int main()
    {
       A a;
       B b;
       b.f3(5);   //不产生名字遮蔽的函数 
       b.f1('a'); //调用基类中的 f1(char)
       cout<<"----------"<<endl;
       b.f1();  //调用派生类中的 f1()
    }
    
  • override final管理虚函数
    • void f() override {…}
      override 说明符说明该函数是对其基类原型一致的虚函数的重定义 主打一个标识作用避免混乱
    • virtual void f() final {…}
      final 限定符限定了这就是最后一个虚函数 其派生类不能再重定义该虚函数void f();

十七、作用域内枚举

  • 传统的枚举存在一些问题
    enum egg {Small,Medium,Large,Jumbo}
    enum t_shirt {Small,Medium,Large,Jumbo}
  • egg Small和t_shirt Small位于相同的作用域内,他们将发生冲突,为避免这种问题,C++11提供了一种新枚举
    其枚举的作用域为类:
    enum class egg {Small,Medium,Large,Jumbo}
    enum class t_shirt {Small,Medium,Large,Jumbo}
  • 也可使用struct代替class. 无论用哪种方式, 都需要使用枚举名来限定枚举量:
    egg choice = egg::Large
    t_shirt Floyd = t_shirt::Large
  • C++11 还提高了作用域内枚举的类型安全. 在有些情况下, 常规枚举将自动转换为整型, 但作用域内枚举不能隐式地转换为整型

To be continue…


总结

学而不思则罔,思而不学则殆

C++17新属性详解 C++17的入选特性有:(1).非类型模板参数的 auto      模板参数分为两种,一种是类型模板参数,也是我们用得最多的一种:template &lt;typename T, typename U&gt; auto add(T t, U u) { return t+u; }     里面的 T 和 U 都是类型模板参数。另一种是非类型模板参数,它可以让不同的字面量成为模板的参数:te... 阅读详情

相关推荐

[c++17] --- c++17相对于c++11的新增特性

C++11引入了许多现代C++特性,例如lambda表达式、自动类型推导、智能指针、移动语义、constexpr等。这些特性使得C++更加现代化、简洁、高效、安全,而C++17在C++11的基础上继续引入了一些新的特性和改进,例如结构化绑定、if constexpr、内联变量、类模板参数推导、并行算法等,进一步提高了C++的开发效率和代码质量。 Note: 本专栏默认读者已了解c++11新特性,通过对比c++11,总结c++17新特性。

M2嵌入式 1200

C++17 新特性介绍

C++17 新特性介绍

langshanglibie的专栏 8708

C++11、C++14 和 C++17 标准新特性详解

特性C++11C++14C++17auto类型推导✅范围 for 循环✅Lambda 表达式✅✅✅智能指针✅移动语义✅nullptr✅统一初始化{}✅✅constexpr✅✅✅✅数字分隔符✅结构化绑定✅✅✅文件系统库✅。

h a 6 6 6 c k 为 你解决复杂难题! 439

C++17入门经典

C++17入门经典第1章 基本概念基本数据类型处理基本数据类型决策数组和循环指针和引用操作字符串定义函数函数模板程序文件和预处理指令 第1章 基本概念 基本数据类型 处理基本数据类型 决策 数组和循环 指针和引用 操作字符串 定义函数 函数模板 程序文件和预处理指令 ...

CC977的博客 5361

C++语言标准及新特性深入解析

本文深入探讨了C++语言标准的设置方法和C++11/14/17/20中引入的一系列新特性。通过实例演示了如何在不同环境下设置C++标准,同时介绍了C++11中类成员初始化语法和std::initializer_list<T>的使用,以及C++17中注解标签的用法和C++11新增的关键字如final、override的详细解释。

weixin_30205153的博客 345

保姆级教程:用GD32F470的EXMC Bank0 Region0与FPGA实现16位并行通信(附完整代码)

本文详细介绍了如何利用GD32F470的EXMC Bank0 Region0与FPGA实现16位并行通信,涵盖硬件连接、寄存器配置、时序优化及FPGA接口设计等关键步骤。通过实例代码和调试技巧,帮助开发者快速掌握高效数据交互技术,提升嵌入式系统设计能力。

weixin_26918763的博客 226

【C++】基础:C++11-14-17常用新特性介绍

C++11-14-17常用新特性介绍

DevFrank的博客 1127

Icarus Verilog和GTKwave使用简析

本简要介绍Icarus Verilog和GTKwave的使用,使用iverilog(Icarus Verilog)编译仿真verilog而使用GTKWave查看仿真数据波形下载安装 基本使用 iverilog使用 GTKWave使用 iverilog常用选项 使用库 注意

husipeng86的专栏 1万+

C++11/14/17常用新特性

这些新特性极大地提升了 C++ 语言的表达能力和编程效率,使得代码更简洁、易读且高效。根据项目需求,你可以逐步引入这些特性来优化现有代码或开发新的项目。

zl15675013251的博客 1885

FinRL实战:5步搞定股票量化交易策略(附Python代码)

本文详细介绍了如何使用FinRL框架,通过5个步骤构建基于深度强化学习的股票量化交易策略。内容涵盖从环境搭建、数据获取与预处理,到交易环境构建、模型训练(如PPO算法),以及策略回测评估与优化的完整流程,并提供了可运行的Python代码示例,帮助读者快速入门AI驱动的量化交易。

weixin_29062865的博客 626

Qt实战:用QCustomPlot实现毫秒级时间轴数据动态刷新(附完整代码)

本文深入探讨了在Qt框架下,如何利用QCustomPlot库高效实现毫秒级时间轴数据的动态刷新与实时可视化。针对工业物联网等高频数据场景,文章提供了从原理剖析、核心代码实现到性能优化的完整实战指南,包括滑动窗口管理、双定时器策略及坐标轴精确配置,帮助开发者构建稳定、流畅的专业级监控界面。

qsc90123456的博客 758

C++新特性01_ c++11 & 14 & 17(C++新特性、学习网站推荐、本栏目介绍的目录)

前面介绍了C++基础知识,目前的C++的新语法风格与旧的可能会完全不同,给我们机会更方便的写代码,本部分将会对新的内容进行介绍。这是原作者喜欢,比较好的查看C++语法的网站(11,14是普遍使用的),本书介绍的也不是特别细致,本栏目会以该目录为原型结合实际进行详细讲解。

分享学习成长历程,Winter is coming! 463

硬件笔记——HDMI接口

HDMI可以同时传输视频信号、音频信号和控制信号。其中高速信号有四组:从Data TX到Data RX有3组差分数据信号(TMDS_DATA0~2)和一组差分时钟信号(TMDS_CLOCK)。以及若干低速线。脚编号信号名称信号类型功能说明1热插拔检测连接状态检测。接收端通过此引脚向源端发送高电平信号,通知其已连接并可以开始读取EDID。2UTILITY车载用途悬空3TMDS数据通道2差分信号正极,用于传输视频像素、音频和数据包。4TMDS数据2屏蔽。

2501_94625908的博客 537

c++ 常用新特性总结【c++11】,【c++14】,【c++17】,【c++20】

一文总结了c++常见的新特性,在c++推出的新特性中,c++11无疑是最具影响力的,其次是c++17与c++14。

Ray 6221

深入理解梅尔刻度、梅尔滤波器组和梅尔时频谱图

读取一段音频,使用短时傅里叶变换,得到普通的时频谱图,然后绘制梅尔滤波器组,值得注意的是,librosa的梅尔滤波器组函数还带有权重归一化功能,即对一个三角形滤波器的每个权重,都除以该三角形的面积,如果不希望进行该归一化,设置参数。从公式可见,对数部分可以以自然对数为底数,也可以以10为底数,不同的底数对应不同的系数,要确定当前的系数,只需要代入(1000Hz, 1000mel)即可。其中,m是当前滤波器的序号,表征了当前的时间段,k是当前频率的序号,表征了当前正在对哪一频率的。

DEDSEC_Roger的博客 1万+

Python-增加b站视频播放量

如果条件允许的情况下,有代理API更好,每次请求url调用API也更方便。如果没有的话,也不用担心,网上本身也有着很多免费国内代理,不过代理是否可用需要自己花点时间去检测,检测方法笔者这里就不详述了,后面有时间的话会单独针对这一块做详细说明。在开始之前,需准备代理IP。总之,在每次请求url的时候,随机带入某个IP防止被平台检测,便于达到增加播放量预期。大致思路就是针对你的目标代理平台进行IP抓取,并存入到自己本地的IP代理池。本文是该专栏的第1篇,后面会持续更新对应平台干货知识,记得关注。

Python进阶专栏《爬虫实战进阶》,《Pyppeteer从入门到精通》原创作者 5209

c++17新特性_不知道C++这七大特性,绝对枉为圈中人

全文共3039字,预计学习时长6分钟来源:Pexels作为一种计算机语言,C++经历了许多发展变化。当然,这些改变并不是一蹴而就的。C++曾经缺乏活力与创新,因此很不受欢迎。但是在C++标准委员会决定加速发展这个语言之后,形势发生了改变。2011年起,C++一跃成为了具有活力、不断演进、广受喜爱的计算机语言。C++蜕变后也并没有简单多少,仍是最难的编程语言之一。但是,C++确实比之前更加人性化了。...

weixin_33514475的博客 1006

《测控仪器设计课后习题答案-浦昭邦-王宝光

《测控仪器设计课后习题答案-浦昭邦-王宝光 1.1. 测控仪器的概念是什么? 测控仪器则是利用测量和控制的理论,采用机、电、光各种计量测试原理及控制系统与计算机相结合的一种范围广泛的测量仪器。 1.2. 为什么说测控仪器的发展与科学技术发展密切相关? 仪器仪表的用途和重要性— 遍及国民经济各个部门,深入到人民生活的各个角落,仪器仪表中的计量测试仪器与控制仪器统称为测控仪器,可以说测控仪器的水平是科学技术现代化的重要标志。 仪器仪表的用途: 在机械制造业中:对产品的静态与动态性能测试;加工过程的控制与监测;设

无名何许人的博客 2976

C++11、C++14、C++17、C++20新特性代码示例

本文全面梳理了C++11到C++20各版本的核心新特性。C++11引入自动类型推导、智能指针、移动语义、并发支持等;C++14新增泛型Lambda、变量模板、constexpr扩展等;C++17提供结构化绑定、文件系统库、optional/variant等实用工具;C++20则带来模块、协程、概念约束等重大革新。每个特性均配有代码示例,涵盖语法改进、性能优化和现代编程范式,帮助开发者系统掌握现代C++的核心功能演进。

汪洋中的一条船 1086

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

C++ 11-14-17-20 新特性变化

扩展用法,可用于定义内联变量,功能与内联函数相似。inline可避免函数或变量多重定义的问题,如果已定义相同的函数或变量(且该函数或变量声明为inline),编译器会自动链接到该函数或变量。

lovely_yoshino的博客 1万+

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

c++ 11 14 17 20后的新特性总结(持续更新)

c++ 11 14 17 20后的新特性 c++11 1.auto auto 不能用于函数传参、auto 还不能用于推导数组类型 2.lambda:匿名函数、闭包(闭包就是能够读取其他函数内部变量的函数-定义在一个函数内部的函数) Lambda 表达式的基本语法如下: [捕获列表](参数列表) mutable(可选) 异常属性-> 返回类型{ // 函数体 } 3.右值引用 c++14 4.结构化绑定 5.std::thread 6.std::regex 7.concept 8.nullptr 取代

Garden的博客 9401

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

好书推荐《C++17 in Detail》

无意中发现作者的博客(https://www.cppstories.com/)和这本书。这本书算是对C++17新增特性较为全面的介绍,而且从实战出发,不流于语法细枝末节,简洁清晰,可以作为Scott Meyers那本非著名的《Effective Modern C++》的补充。 C++ 17是对C++语言的重大更新,引入了许多新的语言特性,这本书涵盖不少源代码,每个代码示例都会详细解释,而且本书的代码示例非常好,可读性强而且简单易懂。

猫哥 的沉淀、积累、总结。天天学习,好好向上...c/c++,嵌入式 linux,Android,HarmonyOS,AIOT) 4426

现代C++语言(C++11/14/17)特性总结和使用建议

C++语言在历史上经过了很多次的演进。最早的时候,C++语言没有模板、STL、异常等特性,之后加入这些特性形成大多数人所熟悉的C++98/03标准。在此之后,C++经过10多年又孕育出了拥有众多革命性变化的C++11标准(在标准正式发布前,被称为C++0x)。C++11包括了约140个新特性和约600个缺陷的修正。由于其变化实在太大,被很多人称为“现代C++语言”(Modern C++ Language),以和之前的经典C++语言作区分。在C++11后,标准委员会又发布了C++14和C++17标准,其内容大

bigchen 7086

C++11 14 17 20 23新特性

C++11 14 17 20 23新特性

A_New_World的博客 1235

C++实用新特性

C++实用新特性

Balaam' s blogs 362

现代 c++ 一:c++11 ~ c++23 新特性汇总

从 c++11 开始,每 3 年发布一个新版本,到今年(2024)已经有 5 个版本了,分别是 c++11、c++14、c++17、c++20、c++23,这 5 个版本引入了上百个新的语言特性和新的标准库特性。“平铺(flat)”容器适配器:std::flat_map、std::flat_multimap、std::flat_set、std::flat_multiset。新的标准属性:[[maybe_unused]]、[[nodiscard]] 和 [[fallthrough]]。

wuxiaobingandbob的专栏 1055
上一篇: c++与c的基本类型的赋值表达式的返回值的区别
下一篇: C++中的const, constexpr, consteval, constinit 汇总
Computer_Tech
博客等级 码龄3年 573粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值