如何解决将 make_shared 与 emplace_back 和 push_back 一起使用 - 有什么区别吗?
some_vector.push_back(make_shared<ClassName>());
some_vector.emplace_back(make_shared<ClassName>());
我想检查一下我的理解是否正确,对于 make_shared
以及通常对于返回对象的所有其他函数,这两个调用是相同的。这里 make_shared
将创建一个新的 shared_ptr
,然后这个指针将在 push_back
和 emplace_back
中移动到容器中。这是正确的,还是会有一些不同?
解决方法
vector<T>::push_back
有一个 T&&
重载,它的作用与 vector<T>::emplace_back
T&&
版本相同。
区别在于 emplace_back
会将任何参数集完美转发到 T
的构造函数,而 push_back
只接受 T&&
或 T const&
.当您实际传递 T&&
或 T const&
时,它们行为的标准规范是相同的。
我想在 Yakk 的回答中添加一个小细节。
emplace_back
情况下的参数转发可能会引入令人怀疑的可怕错误 - 即使对于共享指针的向量 - 如果不特别小心使用,请参见例如
#include <vector>
#include <memory>
struct SimpleStruct {};
auto main() -> int
{
std::vector<std::shared_ptr<SimpleStruct>> v;
SimpleStruct a;
v.emplace_back(std::addressof(a)); // compiles,UB
v.push_back(std::addressof(a)); // fails to compile
}
是的,这是一种极端的例子,因为像这样的代码在使用时应该特别小心或在一般情况下受到质疑,但它强调,只有在没有复制对象已经在手上,它的唯一目的是添加到向量中,所有常见的复制/移动构造情况请参考 emplace_back
。如果语言/标准库可以为 push_back
强制从头开始,即只接受自定义的非复制/移动构造函数以实现这种清晰的分离,但即使它以可接受的方式成为可能,那就太好了,它会与许多模板上下文场景(快进)发生冲突,并且容易出错的用法仍然是可能的,尽管更加明确。
根据我上面的例子,代码重构是一个值得怀疑的重点。简单想象一下,前面的代码使用了原始指针,即实际的底层错误已经存在并被 emplace_back
-usage 隐藏了。它也会被 emplace_back
-usage 隐藏在那里,但不会在您将代码更新为共享指针方式后立即隐藏。
即使它与您的特定特定用例无关,我认为这里值得一提,因为人们应该对两种方法之间的潜在差异完全有信心。
感谢 Human-Compiler 在评论中提及我在此处使用的错误术语。
,要理解这个问题,让我们首先考虑调用 std::make_shared<class_type>()
,
它返回临时对象,这意味着 Xvalue
是一个可以重用资源的 eXpiring
值。现在让我们看看这两种情况,
some_vector.push_back(make_shared<ClassName>());
std::vector
有两个 push_back
重载,其中之一接受 rvalue
引用,即constexpr void push_back( T&& value );
这意味着 value
被移动到新元素中,但是如何呢? rvalue
的 push_back
重载将通过调用 shared_ptr( shared_ptr&& r ) noexcept;
移动构造新值,r
的所有权将被取得,r
变为空。
>
some_vector.emplace_back(make_shared<ClassName>());
在 emplace_back( Args&&... args )
元素是通过 std::allocator_traits::construct
通过完美转发 args..
到 std::forward<Args>(args)...
构造的,这意味着 rvalue
将完美转发并导致相同的移动构造函数 shared_ptr( shared_ptr&& r ) noexcept;
被调用。
结论是,push_back
和 emplace_back
具有相同的效果。
但是上面解释的并没有发生,因为compiler
进入图片,它做了什么,它执行优化,它意味着而不是创建临时对象并将它们移动到其他对象中,它直接创建对象地方。
同样,这两种情况的结果是一样的。
下面包含编译器优化理论的支持代码,如您所见,输出仅打印一个构造函数调用。
#include <iostream>
using std::cout;
using std::endl;
class Object{
public:
explicit Object(int );
Object(const Object& );
Object(Object&& );
};
Object::Object(int ){
cout<< __PRETTY_FUNCTION__<< endl;
}
Object::Object(const Object& ){
cout<< __PRETTY_FUNCTION__<< endl;
}
Object::Object(Object&& ){
cout<< __PRETTY_FUNCTION__<< endl;
}
int main(){
[[maybe_unused]] Object obj(Object(1));
}
输出:Object::Object(int)
-
some_vector.push_back(make_shared<ClassName>());
右值引用传递给函数,push_back 只是调用 emplace_back。void push_back(value_type&& __x) { emplace_back(std::move(__x)); }
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。