• C++11最新特性总结


    自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:

    https://www.cnblogs.com/bclshuai/p/11380657.html

     

    目录

    1      C++11新特性

    1.1          关键字和新语法

    1.1.1     auto关键字

    1.1.2     空指针关键字nullptr

    1.2          新增STL容器

    1.2.1     std::array数组

    1.2.2     std::forward_list

    1.2.3     std::unordered_map无序哈希map

    1.2.4     std::unordered_set无序容器

    1.3          多线程

    1.3.1     std::thread类

    1.3.2     自带锁的原子性变量std::atomic

    1.3.3     条件等待std::condition_variable

    1.4          智能指针std::shared_ptr

    1.4.1     std::shared_ptr

    1.4.2     std::weak_ptr

    1.4.3     std::function、std::bind

    1.5          右值引用和move

    1.5.1     定义

    1.5.2     右值引用的作用

    1.5.3     深拷贝代码实例

    1.5.4     右值引用实现转移构造函数和转移赋值操作符重载

    1.5.5     std::move将不在使用的变量转换为右值引用

    1.5.6     提高交换函数的性能

    1.5.7     总结

    1.6          std::forward参数传递的完美转发

    1.6.1     std::forward作用

    1.6.2     实例说明

    1.6.3     std::forward原理剖析

    1.6.4     总结

    1       C++11新特性

    1.1  关键字和新语法

    1.1.1         auto关键字

    自动变量推导,用auto去定义变量,会根据赋值去自动推导变量类型。不用在去根据数据类型去定义变量。

    例如

    auto AddTest(int a, int b)

    {

        return a + b;

    }

    int main()

    {

        auto index = 10;

        auto str = "abc";

        auto ret = AddTest(1,2);

        std::cout << "index:" << index << std::endl;

        std::cout << "str:" << str << std::endl;

        std::cout << "res:" << ret << std::endl;

    }

    输出

     

    但是auto不能在类定义中声明函数

    class Test

    {

    public:

        auto TestWork(int a ,int b);

    };

    auto变量也可以作为STL容器的迭代器,放入for循环中去使用。

    int main()

    {

        int numbers[] = { 1,2,3,4,5 };

        std::cout << "numbers:" << std::endl;

        for (auto number : numbers)

        {

            std::cout << number << std::endl;

        }

    }

    1.1.2         空指针关键字nullptr

    NULL的宏定义是0,但是0是一个整数,如果重载函数形参为int* n和int n,如果传入NULL不知道是调用哪个重载函数。nullptr就是空指针的,这样就会调用int* n。

    class Test

    {

    public:

        void TestWork(int index)

        {

            std::cout << "TestWork 1" << std::endl;

        }

        void TestWork(int * index)

        {

            std::cout << "TestWork 2" << std::endl;

        }

    };

    int main()

    {

        Test test;

        test.TestWork(NULL);

        test.TestWork(nullptr);

    }

    输出

     

     

    1.2  新增STL容器

    1.2.1         std::array数组

    std::array和数组一样是存储在栈上,封装了一些函数,size(),Empty(),front,back(), at(),访问可以避免越界。而vector是存储在堆上,所以array的访问速度更快。std::array定义时固定长度,和数组一样,vector是变动长度。

    实例代码

    #include <array>

    int main()

    {

        std::array<int, 4> arrayDemo = { 1,2,3,4 };

        std::cout << "arrayDemo:" << std::endl;

        for (auto itor : arrayDemo)

        {

            std::cout << itor << std::endl;

        }

        int arrayDemoSize = sizeof(arrayDemo);

        std::cout << "arrayDemo size:" << arrayDemoSize << std::endl;

        return 0;

    }

    输出结果

     

    1.2.2         std::forward_list

    内部实现是单向链表, list是双向链表,单向链表只有一个方向的指针,所以反向操作不支持,在尾部插入和删除的操作不支持,例如push_back(),back(),因为是单向,所以从头部插入和删除元素效率更高,且节省保存反向指针的空间。也不支持size()函数,这也是为了增加效率,可以通过distance(c.begin(),c.end())来得到forward_list的大小,这将消耗一个线性时间。链表相对于vector内部实现为数组,插入和删除效率更高。

    示例代码:

    #include <forward_list>

    int main()

    {

        std::forward_list<int> numbers = {1,2,3,4,5,4,4};

        std::cout << "numbers:" << std::endl;

        for (auto number : numbers)

        {

            std::cout << number << std::endl;

        }

        numbers.remove(4);

        std::cout << "numbers after remove:" << std::endl;

        for (auto number : numbers)

        {

            std::cout << number << std::endl;

        }

        return 0;

    }

    输出结果

     

    1.2.3         std::unordered_map无序哈希map

    std::unordered_map与std::map用法基本差不多,但STL在内部实现上有很大不同,std::map使用的数据结构为二叉树,而std::unordered_map内部是哈希表的实现方式,哈希map理论上查找效率为O(1)。但在存储效率上,哈希map需要增加哈希表的内存开销。哈希内部实现是数组,hash算法得出键在数组中的索引值,然后直接存入数组的对应位置,取数据时也是计算索引值,然后根据索引值直接访问数组的元素。但是存在hash冲突的问题,可以通过四种方法:开发地址法:线性探测、再平方探测、伪随机探测,链式地址法,建立公共溢出区,再哈希法。参考文献:https://www.cnblogs.com/higerMan/p/11907117.html

    使用实例

    //webset address: http://www.cplusplus.com/reference/unordered_map/unordered_map/bucket_count/

    #include <iostream>

    #include <string>

    #include <unordered_map>

    int main()

    {

        std::unordered_map<std::string, std::string> mymap =

        {

            { "house","maison" },

            { "apple","pomme" },

            { "tree","arbre" },

            { "book","livre" },

            { "door","porte" },

            { "grapefruit","pamplemousse" }

        };

        unsigned n = mymap.bucket_count();

        std::cout << "mymap has " << n << " buckets. ";

        for (unsigned i = 0; i<n; ++i)

        {

            std::cout << "bucket #" << i << " contains: ";

            for (auto it = mymap.begin(i); it != mymap.end(i); ++it)

                std::cout << "[" << it->first << ":" << it->second << "] ";

            std::cout << " ";

        }

        return 0;

    }

    输出结果

     

    1.2.4         std::unordered_set无序容器

    std::unordered_set的数据存储结构也是哈希表的方式结构,std::unordered_set在插入时不会自动排序,而std::set会自动排序,内部值不会重复,内部实现是红黑树。哈希表的存储和查找性能(最好O(1),最差根据不同冲突方法而定)一般高于红黑树(log2N,N是元素的数量)。

    示例程序

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html#include <iostream>

    #include <string>

    #include <unordered_set>

    #include <set>

    int main()

    {

        std::unordered_set<int> unorder_set;

        unorder_set.insert(7);

        unorder_set.insert(5);

        unorder_set.insert(3);

        unorder_set.insert(4);

        unorder_set.insert(6);

        std::cout << "unorder_set:" << std::endl;

        for (auto itor : unorder_set)

        {

            std::cout << itor << std::endl;

        }

        std::set<int> set;

        set.insert(7);

        set.insert(5);

        set.insert(3);

        set.insert(4);

        set.insert(6);

        std::cout << "set:" << std::endl;

        for (auto itor : set)

        {

            std::cout << itor << std::endl;

        }

    }

    1.3  多线程

    C++11中,引入了boost库中的多线程部分内容,形成C++标准

    1.3.1         std::thread类

    std::是一个线程类,可以传入多个参数的线程函数,不像之前创建线程参数只能是固定的。

    #include <thread>

    void threadfun1()

    {

        std::cout << "threadfun1 - 1 " << std::endl;

        std::this_thread::sleep_for(std::chrono::seconds(1));

        std::cout << "threadfun1 - 2" << std::endl;

    }

    void threadfun2(int iParam, std::string sParam)

    {

        std::cout << "threadfun2 - 1" << std::endl;

        std::this_thread::sleep_for(std::chrono::seconds(5));

        std::cout << "threadfun2 - 2" << std::endl;

    }

    int main()

    {

        std::thread t1(threadfun1);

        std::thread t2(threadfun2, 10, "abc");

        t1.join();

        std::cout << "join" << std::endl;

        t2.detach();强退线程

        std::cout << "detach" << std::endl;

    }

    输出结果

     

    1.3.2         自带锁的原子性变量std::atomic

    以前多线程访问同一个变量时,为了避免多线程同时读写,所以要加上锁,临界区等,现在可以通过自带加锁功能的原子型变量atomic_int,

    实例程序

    #include <thread>

    #include <atomic>

    #include <stdio.h>

    std::atomic_bool bIsReady = false;

    std::atomic_int iCount = 100;

    void threadfun1()

    {

        if (!bIsReady) {

            std::this_thread::yield();

        }

        while (iCount > 0)

        {

            printf("iCount:%d ", iCount--);

        }

    }

    int main()

    {

        std::atomic_bool b;

        std::list<std::thread> lstThread;

        for (int i = 0; i < 10; ++i)

        {

            lstThread.push_back(std::thread(threadfun1));

        }

        for (auto& th : lstThread)

        {

            th.join();

        }

    }

    1.3.3         条件等待std::condition_variable

    通过等待锁unique_lock挂起线程,使线程不占用CPU的时间,当有任务时,通过notify_all()函数唤醒等待,执行任务。

    // condition_variable example

    #include <iostream>           // std::cout

    #include <thread>             // std::thread

    #include <mutex>              // std::mutex, std::unique_lock

    #include <condition_variable> // std::condition_variable

    std::mutex mtx;

    std::condition_variable cv;

    bool ready = false;

    void print_id(int id) {

        std::unique_lock<std::mutex> lck(mtx);

        while (!ready) cv.wait(lck);

        // ...

        std::cout << "thread " << id << ' ';

    }

    void go() {

        std::unique_lock<std::mutex> lck(mtx);

        ready = true;

        cv.notify_all();

    }

    int main()

    {

        std::thread threads[10];

        // spawn 10 threads:

        for (int i = 0; i<10; ++i)

            threads[i] = std::thread(print_id, i);

        std::cout << "10 threads ready to race... ";

        go();                       // go!

        for (auto& th : threads) th.join();

        return 0;

    }

    输出结果

     

    1.4  智能指针std::shared_ptr

    智能指针封装了内存的申请和删除,模拟了java的内存回收机制。自动删除内存,通过创建引用句柄指向内存空间,同时通过计数count统计指向内存的引用数量。当count为0时,自动删除内存。相互引用的情况下,会造成内存泄露,所以有了std::weak_ptr指针,指向内存,不会增加计数count。count降为0时,也会销毁内存。

     

    1.4.1         std::shared_ptr

    使用std::shared_ptr<Test> p1 = std::make_shared<Test>()函数来创建对像。

    #include <memory>

    class Test

    {

    public:

        Test()

        {

            std::cout << "Test()" << std::endl;

        }

        ~Test()

        {

            std::cout << "~Test()" << std::endl;

        }

    };

    int main()

    {

        std::shared_ptr<Test> p1 = std::make_shared<Test>();//新建内存

        std::cout << "1 ref:" << p1.use_count() << std::endl;//1

        {

            std::shared_ptr<Test> p2 = p1;//创建新的引用,count变为2

            std::cout << "2 ref:" << p1.use_count() << std::endl;//2

    }

    // p2销毁,count变为1

        std::cout << "3 ref:" << p1.use_count() << std::endl;//输出1

        return 0;

    }

     

    1.4.2         std::weak_ptr

    通过std::weak_ptr<classname>创建对象的弱引用,不会增加count计数值,通过弱引用的函数lock(),可以将弱引用转换为强引用。  std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock();

    //示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html#include <memory>

    class TestB;

    class TestA

    {

    public:

        TestA()

        {

            std::cout << "TestA()" << std::endl;

        }

        void ReferTestB(std::shared_ptr<TestB> test_ptr)

        {

            m_TestB_Ptr = test_ptr;#弱引用指向,计数没有加

        }

        void TestWork()

        {

            std::cout << "~TestA::TestWork()" << std::endl;

        }

        ~TestA()

        {

            std::cout << "~TestA()" << std::endl;

        }

    private:

        std::weak_ptr<TestB> m_TestB_Ptr;

    };

    class TestB

    {

    public:

        TestB()

        {

            std::cout << "TestB()" << std::endl;

        }

        void ReferTestB(std::shared_ptr<TestA> test_ptr)

        {

            m_TestA_Ptr = test_ptr;

        }

        void TestWork()

        {

            std::cout << "~TestB::TestWork()" << std::endl;

        }

        ~TestB()

        {//弱引用转换为强引用,对象A的count加1等于2

            std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock();

            tmp->TestWork();

            std::cout << "2 ref a:" << tmp.use_count() << std::endl;

            std::cout << "~TestB()" << std::endl;

        }

        std::weak_ptr<TestA> m_TestA_Ptr;

    };

    int main()

    {

        std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();//创建强引用A

        std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();//创建强引用B

        ptr_a->ReferTestB(ptr_b);//弱引用指向B

        ptr_b->ReferTestA(ptr_a);弱引用指向A

        std::cout << "1 ref a:" << ptr_a.use_count() << std::endl;

        std::cout << "1 ref b:" << ptr_a.use_count() << std::endl;

        return 0;

    }

    1.4.3         std::function、std::bind

    以前的方式使用函数指针的方式,将函数指针以实参的形式传给定义了函数形参的函数。std::function可以直接定义函数形参,传入函数或者lamla表达式,函数内部调用传入的函数,返回结果。如果要将类A中的函数作为实参传给类B中的函数形参, 在B中调用A中的函数,可以std::bind函数去绑定。

    (1)函数指针

    class Test

    {

    public:

        typedef int(*FunType)(int, int);

        void Add(FunType fun,int a,int b)

        {

            int sum = fun(a, b);

            std::cout << "sum:" << sum << std::endl;

        }

    };

    #include <functional>

    #include <iostream>

    #include "Test.h"

    int add(int a,int b)

    {

        return a + b;

    }

    int main()

    {

        Test test;

    test.Add(add, 1, 2);

        return 0;

    }

    (2)引用其他类的函数

    #定义类Test,用std::function声明函数形参

    class Test

    {

    public:

        void Add(std::function<int(int, int)> fun, int a, int b)

        {

            int sum = fun(a, b);

            std::cout << "sum:" << sum << std::endl;

        }

    };

    1.5  右值引用和move

    参考文献:https://winsoft666.blog.csdn.net/article/details/78520237

    1.5.1         定义

    左值指的是变量,可以在多行语句中使用,有变量指向存储空间。右值是指临时的对象,它们只在当前的语句中有效。例如MyString a; a = MyString("Hello");a就是左值,MyString("Hello")就是右值,MyString("Hello")会调用构造函数,复制给a时,又会调用复制构造函数,语句执行完后,MyString("Hello")创建的对象就会调用析构函数销毁掉。为了避免MyString("Hello")销毁,和a对象的构造,直接将MyString("Hello")创建的对象申请的地址空间转移给a,提高程序运行的效率。就可以通过右值引用来实现。

    在C++11之前,右值是不能被引用的,如:

    int &a = 1;   // error C2440: “初始化”: 无法从“int”转换为“int &”

    我们最多只能用常量引用来绑定一个右值,如:

    const int &a = 1;

    在C++11中,我们可以右值引用,使用&&来实现:

    int &&a = 1;

    注意:这里a的类型是右值引用类型(int &&),但是如果从左值和右值的角度区分它,它实际上是个左值。因为可以对它取地址,而且它还有名字,是一个已经命名的左值。

    1.5.2         右值引用的作用

    是消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。

    1.5.3         深拷贝代码实例

    class MyString {

    private:

           char* _data;

           size_t   _len;

           void _init_data(const char *s) {

                  _data = new char[_len + 1];

                  memcpy(_data, s, _len);

                  _data[_len] = '';

           }

    public:

           MyString() {

                  _data = NULL;

                  _len = 0;

           }

           MyString(const char* p) {

                  _len = strlen(p);

                  _init_data(p);

           }

           MyString(const MyString& str) {

                  _len = str._len;

                  _init_data(str._data);

                  std::cout << "Copy Constructor is called! source: " << str._data << std::endl;

           }

           MyString& operator=(const MyString& str) {

                  if (this != &str) {

                         _len = str._len;

                         _init_data(str._data);

                  }

                  std::cout << "Copy Assignment is called! source: " << str._data << std::endl;

                  return *this;

           }

           virtual ~MyString() {

                  if (_data != NULL) {

                         std::cout << "Destructor is called! " << std::endl;

                         free(_data);

                  }

           }

    };

    int main() {

           MyString a;

           a = MyString("Hello"); //调用构造函数创建MyString("Hello"),调用赋值构造函数创建a。

    // MyString("Hello")对象销毁,调用析构函数

           std::vector<MyString> vec;

           vec.push_back(MyString("World")); //调用构造函数创建MyString("World")

    //调用复制构造函数创建变量存入vect;

    }

    输出结果:

    Copy Assignment is called! source: Hello

    Destructor is called!

    Copy Constructor is called! source: World

    Destructor is called!

    Destructor is called!

    Destructor is called!

    整个过程中构造函数创建了两次,复制构造函数调用一次,赋值构造函数调用一次,一共创建了4个对象。两个临时对象MyString("Hello")和MyString("World")创建后即销毁。为了减少对象的创建,我们将复制构造函数和复制构造函数通过右值引用的方式来定义。

    1.5.4         右值引用实现转移构造函数和转移赋值操作符重载

           MyString(MyString&& str) {

                  std::cout << "Move Constructor is called! source: " << str._data << std::endl;

                  _len = str._len;

                  _data = str._data;

                  str._len = 0;

                  str._data = NULL;   // ! 防止在析构函数中将内存释放掉

           }

           MyString& operator=(MyString&& str) {

                  std::cout << "Move Assignment is called! source: " << str._data << std::endl;

                  if (this != &str) {

                         _len = str._len;

                         _data = str._data; //转移了, 并没有重新_init_data

                         str._len = 0;

                         str._data = NULL;  // ! 防止在析构函数中将内存释放掉

                  }

                  return *this;

           }

    输出结果:

    Move Assignment is called! source: Hello

    Move Constructor is called! source: World

    Destructor is called!

    Destructor is called!

    1.5.5         std::move将不在使用的变量转换为右值引用

    如果已知一个命名对象不再被使用,而想对它调用转移构造函数和转移赋值函数,使用函数 std::move将左值引用转换为右值引用。

    void ProcessValue(int& i) {

           std::cout << "LValue processed: " << i << std::endl;

    }

    void ProcessValue(int&& i) {

           std::cout << "RValue processed: " << i << std::endl;

    }

    int main() {

           int a = 0;

           ProcessValue(a);

           ProcessValue(std::move(a));

    }

    输出结果

    LValue processed: 0

    RValue processed: 0

    1.5.6         提高交换函数的性能

    std::move在提高 swap 函数的的性能上非常有帮助,互换函数的定义可以从

    template <class T>

    void swap(T& a, T& b)

    {

           T tmp(a);   // copy a to tmp

           a = b;      // copy b to a

           b = tmp;    // copy tmp to b

    }

    变为如下方式,减少了临时对象的创建。

    template <class T>

    void swap(T& a, T& b)

    {

           T tmp(std::move(a)); // move a to tmp

           a = std::move(b);    // move b to a

           b = std::move(tmp);  // move tmp to b

    }

    1.5.7         总结

    (1)   左值是有地址有名字的变量,右值是临时的将亡变量。int&& a=1;a也是左值。

    (2)   左值引用指向变量,右值引用不能指向左值。int a=0;int&b=a;正确,int&&c=a;错误,编译不过。常量左值引用例外,可以指向右值,const int& d=1;正确。常量引用不可改变。

    (3)   如果一个左值对象不再被使用,或者将要销毁,需要创建新的对象时,将即将销毁的左值对象转移给新的对象,可以用std::move实现,使用函数 std::move将左值引用转换为右值引用。

    1.6  std::forward参数传递的完美转发

    1.6.1         std::forward作用

    std::forward会将输入的参数原封不动地传递到下一个函数中,这个“原封不动”指的是,如果输入的参数是左值,那么传递给下一个函数的参数的也是左值;如果输入的参数是右值,那么传递给下一个函数的参数的也是右值。

    1.6.2         实例说明

    (1)先定义一个类CData,实现左值引用构造和右值引用构造函数。当入参为

    class CData

    {

    public:

           CData(const char* ch) : data(ch)

           {

                  std::cout << "CData(const char* ch)" << std::endl;

           }

           CData(const std::string& str) : data(str) //左值引用构造函数

           {

                  std::cout << "CData(const std::string& str)" << std::endl;

           }

           CData(std::string&& str) : data(str)//右值引用构造函数

           {

                  std::cout << "CData(std::string&& str)" << std::endl;

           }

           ~CData()//析构函数

           {

                  std::cout << "~CData()" << std::endl;

           }

    private:

           std::string data;

    };

    (2)我们在定义一个模板新建函数

    template<typename T>

    CData* Creator(T&& t)

    {

           return new CData(t);

    }

    creator函数因为使用了模板,虽然是右值引用形参T&&,但是可以传入T&,T&&,T三种类型,因为T是模板类型,如果T是string类型,则入参可以是string,string&,string&&,模板有折叠规则:

    string&  &&折叠之后是string&,

    string&& &&折叠之后是string&&,

    string   &&折叠后是string&

    如果定义是CData* Creator(T& t),那么折叠之后是

    string&  &折叠之后是string&,

    string&& &折叠之后是string&,

    string   &折叠后是string&

    总结一下:string&& &&折叠之后是string&&,其他的都是string&;

    (3)创建测试程序

    int main()

    {

        const char* value = "hello";

        std::string str1 = "hello";

        std::string str2 = " world";

        string& str3 = str2;

        string&& str4 = str1 + str2;

        printf("左值 ");

        CData* p1 = Creator(str1);//CData(const std::string& str)

        delete p1;

        printf("右值 ");

        CData* p2 = Creator(str1 + str2);//CData(const std::string& str)

        delete p2;

        printf("左值引用,是个左值 ");

        CData* p3 = Creator(str3);//CData(const std::string& str)

        delete p3;

        printf("右值引用,也是个左值 ");

        CData* p4 = Creator(str4);//CData(const std::string& str)

        delete p4;

        system("pause");

    }

    分别传入实参左值,右值,左值引用,右值引用,传入右值引用之后,输出结果全部是CData(const std::string& str),这就是参数的不完美传递。我们期望的是传入左值,调用左值构造函数,传入右值调用右值构造函数,就可以修改为return new CData(std::forward(t));这样就会调用CData(std::string&& str)转移构造函数。这样就会实现参数的完美传递。

    template<typename T>

    CData* Creator(T&& t)

    {

           return new CData(std::forward<T>(t));

    }

    测试程序输出结果:

    左值

    CData(const std::string& str)

    右值

    CData(std::string&& str)//这里调用了右值引用构造函数

    左值引用,是个左值

    CData(const std::string& str)

    右值引用,也是个左值

    CData(const std::string& str)

    为什么传入右值引用实参?确调用了左值构造函数,因为形参变量t是一个左值。

    1.6.3         std::forward原理剖析

    (1)   forward函数定义

    forward的定义也是模板定义,remove_reference<_Ty>::type是去除引用的类型定义,例如_Ty是int& int&&都会转为int。最后返回(static_cast<_Ty&&>(_Arg));如果_Ty是int&,则根据叠加规则无论传入什么,都返回的是int&。如果_Ty是int或者int&&,则会返回右值引用类型。

    template<class _Ty> inline

           constexpr _Ty&& forward(typename remove_reference<_Ty>::type& _Arg) _NOEXCEPT

           {     // forward an lvalue as either an lvalue or an rvalue

           return (static_cast<_Ty&&>(_Arg));

           }

    template<class _Ty> inline

           constexpr _Ty&& forward(

                  typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT

           {     // forward an rvalue as an rvalue

           static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");

           return (static_cast<_Ty&&>(_Arg));

           }

    (2)remove_reference<_Ty>定义

    remove_reference<_Ty>负责将模板参数类型_Ty的引用去掉,比如int、int&、int&&转换后都变成int,其实现如下

    template<class _Ty>

           struct remove_reference

           {     // remove reference

           typedef _Ty type;

           };

    template<class _Ty>

           struct remove_reference<_Ty&>

           {     // remove reference

           typedef _Ty type;

           };

    template<class _Ty>

           struct remove_reference<_Ty&&>

           {     // remove rvalue reference

           typedef _Ty type;

           };

    由源代码可知 remove_reference<_Ty>::type为模板类型去掉引用的类型。

    (3)static_cast<_Ty&&>(_Arg))模板转换规则

    _Ty是传递给forward函数的类型,可以是T ,T&,T&&。举例int,int&,int&&。_Ty&&也要遵循折叠原则。

    模板参数为引用类型T、T&&时,返回右值引用

    模板参数为引用类型T&时,返回左值引用。

    推导如下:

    _Ty  =>  T

    template<class  T>

    T&& forward(typename T& t) { return (static_cast<T&&>(t)); }

    _Ty = > T&&

    template<class T&&>

    T&& && forward(typename T& t) { return (static_cast<T&& &&>(t)); } 

    ==>  T&&  forward(typename T& t) { return (static_cast<T&& &&>(t)); }

    _Ty = > T&

    template<class T&>

    T&& & forward(typename T& t) { return (static_cast<T& &&>(t)); }

    == > T&  forward(typename T& t) { return (static_cast<T& &&>(t)); }

    1.6.4         总结

    (1)   forward用于参数传递时的保持参数的左值,右值属性,形参用T&&,传递用std::forward<T>(),例如Creator(T&& t){return new CData(std::forward<T>(t));},参数传遵循折叠原则。

    (2)   引用折叠string&& &&折叠之后是string&&,其他的都是string&;

    (3)   forward实现原理是先是去除引用类型remove_reference<_Ty>::type& _Arg,强制转换return (static_cast<_Ty&&>(_Arg)); _Arg是去除引用后的参数类型,例如_Ty 是int,int&,int&&,_Arg都是int&类型,强制转换是折叠之后的强制转换。_Ty如果是int&,则输出是int&,如果是int或者int&&,输出是int&&。

  • 相关阅读:
    程序怎么才能把自己的删除掉?
    Winsock编程入门1.初始化Winsock
    关于83版射雕英雄传
    一个感人的爱情故事(中英对照)
    NT系统的命令
    Delphi小巧的Windows NT服务程序源码
    更改Windows 登录屏幕保护程序
    画鬼最易
    濮水垂钓
    现代工作观
  • 原文地址:https://www.cnblogs.com/bclshuai/p/13932603.html
Copyright © 2020-2023  润新知