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.\n";

    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 << "\n";

    }

    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\r\n" << 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\r\n", 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 << '\n';

}

 

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...\n";

    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] = '\0';

       }

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("左值\n");

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

    delete p1;

    printf("右值\n");

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

    delete p2;

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

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

    delete p3;

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

    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&&。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bclshuai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值