具有左值表达式的std :: vector :: emplace_back

时间:2019-03-18 13:52:24

标签: c++ variadic-templates emplace

emplace_back与某个结构S的左值一起使用是否有意义?

像这样:

std::vector<S> v;
auto s = S(/*...*/);
v.emplace_back(s);

不只是:

v.emplace_back(/* S constructor arguments */);

或者只是对emplace_back的简单滥用,这只是因为const S&(因此是复制构造函数)是Args... argsemplace_back的合法实例而发生的,并没有明确禁止吗?

2 个答案:

答案 0 :(得分:5)

正如您已经说过的那样,传递const S&只会调用复制构造函数。

除非您打算在将s传递给emplace_back之前以某种方式使用它,否则它不一定是明智的。

但是,例如,如果创建s的代码非常长,则可以将其和emplace_back的代码放在单独的行中来提高可读性。编译器非常擅长优化此类情况,并且无论如何都会生成相同的代码(如果复制构造函数为默认值)。基本示例:https://godbolt.org/z/D1FClE

如果它提高了可读性或可维护性,请这样做,否则就没有任何价值。

答案 1 :(得分:1)

如果稍后在代码中不需要s,则它是对emplace_back()函数的滥用。这是因为您正在调用S类的副本构造函数,而不是将参数传递给emplace_back(),后者将使用S中的正确构造函数。

考虑以下代码:

#include <iostream>
#include <vector>

struct S
{
    S()          {std::cout<< "     default ctor" <<std::endl;}
    S(int)       {std::cout<< "     user-def ctor" <<std::endl;}
    S(const S &) {std::cout<< "     copy ctor" <<std::endl;}
    S(S &&)      {std::cout<< "     move ctor" <<std::endl;}
};

int main()
{
    std::vector<S> v;
    v.reserve(5);

    std::cout<< "auto calls: " <<std::endl;
    auto s = S();
    std::cout<<std::endl;

    std::cout<< "emplace_back( s ) calls: " <<std::endl;
    v.emplace_back(s);
    std::cout<<std::endl;

    std::cout<< "emplace_back( std::move(s) ) calls: " <<std::endl;
    v.emplace_back(std::move(s));
    std::cout<<std::endl;

    std::cout<< "emplace_back( S{} ) calls: " <<std::endl;
    v.emplace_back(S{});
    std::cout<<std::endl;

    std::cout<< "emplace_back( ) calls: " <<std::endl;
    v.emplace_back();
    std::cout<<std::endl;

    std::cout<< "emplace_back( 2 ) calls: " <<std::endl;
    v.emplace_back(2);
    std::cout<<std::endl;
}

结果是:

auto calls: 
     default ctor

emplace_back( s ) calls: 
     copy ctor

emplace_back( std::move(s) ) calls: 
     move ctor

emplace_back( S{} ) calls: 
     default ctor
     move ctor

emplace_back( ) calls: 
     default ctor

emplace_back( 2 ) calls: 
     user-def ctor

预留空间用于分配5个S的空间。在不保留空间的情况下,输出将包括从向量对副本ctor的其他调用。

当您仅将参数传递给S的构造函数(在这种情况下,什么都没有)时,emplace_back()会使用向量内部的默认ctor创建一个S对象。

顺便说一句,请参阅example in godbolt(在这种情况下是您的朋友),以了解确切的背景情况。