std::vector::emplace_back 带左值表达式
std::vector::emplace_back with lvalue expression
将 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... args
内部 emplace_back
的合法实例化], 并没有明确禁止?
如您所说,传递 const S&
只会调用复制构造函数。
除非您打算在将 s
传递给 emplace_back
之前以某种方式使用它,否则这不一定是明智的。
但是,如果创建 s
的代码特别长,将它和 emplace_back
的代码放在不同的行中可以提高可读性。编译器非常擅长优化这种情况,并且可能无论如何都会生成相同的代码(如果复制构造函数是默认的)。基本示例:https://godbolt.org/z/D1FClE
如果它提高了可读性或可维护性就去做,否则就没有价值。
如果后面的代码中不需要s
,那么就是误用了emplace_back()
函数。这是因为您正在调用 S
class 的复制构造函数,而不是将参数传递给 emplace_back()
,后者将使用 S
.[=21= 中的正确构造函数]
考虑以下代码:
#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
储备用于分配space 5 S
秒。在不保留 space 的情况下,输出将包括对向量中的复制 ctors 的额外调用。
当您将参数传递给 S
的构造函数时(在本例中,什么也没有),emplace_back()
直接在向量内部使用默认构造函数创建一个 S 对象。
顺便说一句,请查看 example in godbolt 在这些情况下是您的朋友,以确切了解后台发生的情况。
将 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... args
内部 emplace_back
的合法实例化], 并没有明确禁止?
如您所说,传递 const S&
只会调用复制构造函数。
除非您打算在将 s
传递给 emplace_back
之前以某种方式使用它,否则这不一定是明智的。
但是,如果创建 s
的代码特别长,将它和 emplace_back
的代码放在不同的行中可以提高可读性。编译器非常擅长优化这种情况,并且可能无论如何都会生成相同的代码(如果复制构造函数是默认的)。基本示例:https://godbolt.org/z/D1FClE
如果它提高了可读性或可维护性就去做,否则就没有价值。
如果后面的代码中不需要s
,那么就是误用了emplace_back()
函数。这是因为您正在调用 S
class 的复制构造函数,而不是将参数传递给 emplace_back()
,后者将使用 S
.[=21= 中的正确构造函数]
考虑以下代码:
#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
储备用于分配space 5 S
秒。在不保留 space 的情况下,输出将包括对向量中的复制 ctors 的额外调用。
当您将参数传递给 S
的构造函数时(在本例中,什么也没有),emplace_back()
直接在向量内部使用默认构造函数创建一个 S 对象。
顺便说一句,请查看 example in godbolt 在这些情况下是您的朋友,以确切了解后台发生的情况。