C++ 容器处理 左值 右值

C++ 右值

什么是左值,右值?一个形象的表述:出现在“=”左边的就是左值,出现在“=”右边的就是右值。形象,但不严谨。
在C++中被广泛认同的:

左值:可以取地址,有名字的;
右值: 不可以取地址,没有名字的。

C++ 11中的右值由两个概念组成:将亡值(expire value),纯右值(pure Rvalue):

将亡值
C++11新增加跟右值引用相关的表达式。这样的表达式通常是将要被移动的对象。例如:返回右值引用T&& 的函数返回值,std::move()的返回值,转换为T&&的类型转换函数的返回值。

纯右值
临时变量和一些跟对象不相关的值。例如:函数返回的非引用的临时变量;一些运算表达式(1+2)产生临时变量值;跟对象不相关的字面值:2, true;此外类型转换函数返回值,lambda表达式也是纯右值。

C++ 容器处理左值右值

C++ 的容器会根据传入的参数类型,进行左值右值的处理。

从效率上面看 emplace(123) > push_back(move(Bjarne(123)) > push_back(Bjarne(123))

测试代码如下:

#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <shared_mutex>
#include <string>
#include <memory>
#include <vector>


using namespace std;

class Bjarne
{
public:
    Bjarne(int num) :data(new int(num))
    { 
        std::cout << "Construct "  << std::endl; 
    }

    Bjarne(const Bjarne &bjarne) :data(new int(*bjarne.data))
    {
        std::cout << "Copy Construct " << std::endl;
    }

    Bjarne(Bjarne &&bjarne):data(bjarne.data)
    {
        bjarne.data = nullptr;
        std::cout << "Move Construct " << std::endl;
    }

    int getValue() {
        return *data;
    }

    ~Bjarne() {
        delete data; 
        std::cout << "Destruct "  << std::endl;
    }
    int *data;
};

Bjarne getBjarne(){
    return Bjarne(1);
}


int main(int argc, char*argv[])
{
    // emplace(1,2,3) > insert(move(AAA(1,2,3)) > insert(AAA(1,2,3))

    {
        std::cout << "1.0 push_back receive lValue will call Copy Constructor.  " << std::endl;
        std::cout << " " << std::endl;

        std::vector<Bjarne> jackyVec;
        Bjarne bjarne(10);
        jackyVec.push_back(bjarne);

        std::cout << " " << std::endl;
        std::cout << " " << std::endl;
    }

    {
        std::cout << " " << std::endl;
        std::cout << "1.1 emplace_back receive lValue will call Copy Constructor.  " << std::endl;
        std::cout << " " << std::endl;

        std::vector<Bjarne> jackyVec;
        Bjarne bjarne(10);
        jackyVec.emplace_back(bjarne);

        std::cout << " " << std::endl;
        std::cout << " " << std::endl;
    }

    {
        std::cout << " " << std::endl;
        std::cout << "1.2 emplace_back receive rValue will call Move Constructor.  " << std::endl;
        std::cout << " " << std::endl;

        std::vector<Bjarne> jackyVec;
        Bjarne bjarne(10);
        jackyVec.emplace_back(std::move(bjarne));

        std::cout << " " << std::endl;
        std::cout << " " << std::endl;
    }

    {
        std::cout << " " << std::endl;
        std::cout << "1.3 push_back receive rValue will call Move Constructor.  " << std::endl;
        std::cout << " " << std::endl;

        std::vector<Bjarne> jackyVec;
        Bjarne bjarne(10);
        jackyVec.push_back(std::move(bjarne));

        std::cout << " " << std::endl;
        std::cout << " " << std::endl;
    }

    {
        std::cout << " " << std::endl;
        std::cout << "2.0 Construct object in parameter treated as rValue. push_back" << std::endl;
        std::cout << " " << std::endl;

        std::vector<Bjarne> jackyVec;
        jackyVec.push_back(Bjarne(21));
        std::cout << " " << std::endl;
        std::cout << " " << std::endl;
    }

    {
        std::cout << "2.1 Construct object in parameter treated as rValue. emplace_back" << std::endl;
        std::cout << " " << std::endl;
        std::vector<Bjarne> jackyVec;

        jackyVec.emplace_back(Bjarne(21));
        std::cout << " " << std::endl;
        std::cout << " " << std::endl;
    }

    {
        std::cout << " " << std::endl;
        std::cout << "3.0 emplace_back in-place call Constructor  .  " << std::endl;
        std::vector<Bjarne> jackyVec;
        std::cout << " " << std::endl;
        jackyVec.emplace_back(20);   //Bjarne(int bjarne)
        std::cout << " " << std::endl;
        std::cout << " " << std::endl;
    }

    {
        std::cout << " " << std::endl;
        std::cout << "4.0 Return value treated as rValue.  emplace_back " << std::endl;
        std::vector<Bjarne> jackyVec;
        std::cout << " " << std::endl;
        jackyVec.emplace_back(getBjarne());
        std::cout << " " << std::endl;
        std::cout << " " << std::endl;
    }

    {
        std::cout << " " << std::endl;
        std::cout << "4.1 Return value treated as rValue.  push_back " << std::endl;
        std::vector<Bjarne> jackyVec;
        std::cout << " " << std::endl;
        jackyVec.push_back(getBjarne());
        std::cout << " " << std::endl;
        std::cout << " " << std::endl;
    }


    return 0;
}

测试结果:

1.0 push_back receive lValue will call Copy Constructor.  
 
Construct 
Copy Construct 
 
 
Destruct 
Destruct 
 
1.1 emplace_back receive lValue will call Copy Constructor.  
 
Construct 
Copy Construct 
 
 
Destruct 
Destruct 
 
1.2 emplace_back receive rValue will call Move Constructor.  
 
Construct 
Move Construct 
 
 
Destruct 
Destruct 
 
1.3 push_back receive rValue will call Move Constructor.  
 
Construct 
Move Construct 
 
 
Destruct 
Destruct 
 
2.0 Construct object in parameter treated as rValue. push_back
 
Construct 
Move Construct 
Destruct 
 
 
Destruct 
2.1 Construct object in parameter treated as rValue. emplace_back
 
Construct 
Move Construct 
Destruct 
 
 
Destruct 
 
3.0 emplace_back in-place call Constructor  .  
 
Construct 
 
 
Destruct 
 
4.0 Return value treated as rValue.  emplace_back 
 
Construct 
Move Construct 
Destruct 
 
 
Destruct 
 
4.1 Return value treated as rValue.  push_back 
 
Construct 
Move Construct 
Destruct 
 
 
Destruct 

总结

  1. emplace() 只有在传入对象的构造参数的时候才会进行“就地构造”。
  2. emplace() 和push_back()在传入右值的时候,会调用移动构造,效率比拷贝构造更高一些。
  3. emplace() 和push_back()在传入左值的时候,会调用拷贝构造。

写在最后: 这个实验,纠正了我之前的一些错误理解。

引用: (2条消息) C++拷贝构造、移动拷贝构造、左值、右值码灵薯的博客-CSDN博客移动拷贝

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容