如果我有一些返回std::shared_ptr<T>
的函数,我该如何在std::map<U, std::shared_ptr<T>>
中插入该函数调用的结果:用insert
和make_pair
还是只用{ {1}}?
假设我已经知道没有重复的密钥,而且我是单线程的。
emplace
RVO是否可以与std::shared_ptr<Foo> bar() {
return std::make_shared<Foo>();
}
std::map<std::string, std::shared_ptr<Foo>> my_map;
// Which makes more sense?
my_map.emplace("key", bar());
// or
my_map.insert(make_pair("key", bar()));
一起使用?
答案 0 :(得分:3)
我认为是正确的。
我认为RVO只要在编译器中实现就可以正常工作,因为bar()
返回时态对象,并且编译器知道从{{1 }}。
此外,在C ++ 17及更高版本中,在这种情况下,将确保RVO。
Is RVO (Return Value Optimization) applicable for all objects?
What are copy elision and return value optimization?
std :: map :: emplace
在
bar()
,RVO将my_map.emplace("key", bar());
替换为bar()
,并创建了std::make_shared<Foo>()
。
然后将其转发,并在就地构建std::shared_ptr<Foo>
的新元素时将std::shared_ptr<Foo>
的 move-ctor 称为一次。
std :: map ::使用std :: make_pair插入
在
my_map
,RVO再次将my_map.insert(make_pair("key", bar()));
替换为bar()
。
接下来,从N3337的20.3.2开始,到C ++ 11,并纠正了一些小错误,std::make_shared<Foo>()
的类模板为
std::pair
模板ctor在哪里
namespace std{ template<class T1, class T2> struct pair { typedef T1 first_type; typedef T2 second_type; T1 first; T2 second; ... template<class U, class V> pair(U&& x, V&& y); ... } }
效果:构造函数使用
template<class U, class V> pair(U&& x, V&& y);
初始化first
,并使用std::forward<U>(x)
初始化second
。
此外,从20.3.3起
std::forward<V>(y)
返回:
template<class T1, class T2> pair<V1, V2> make_pair(T1&& x, T2&& y);
; ...
对于此定义,pair<V1, V2>(std::forward<T1>(x), std::forward<T2>(y))
的典型实现被认为是这样的:
std::make_pair
,RVO将再次适用于namespace std{
template<class T1, class T2>
inline pair<typename decay<T1>::type, typename decay<T2>::type>
make_pair(T1&& x, T2&& y)
{
return pair<typename decay<T1>::type,
typename decay<T2>::type>
(std::forward<T1>(x), std::forward<T2>(y));
}
}
。
实际上,如果我们使用C ++ 14和{-fno-elide-constructors“编译器选项编译DEMO,而该编译器选项会禁用g ++的RVO,则move-ctor调用的数量只会增加一个。
因此,在std::make_pair
中,我预计会出现以下情况:
std::make_pair("key", bar())
替换为bar()
,std::make_shared<Foo>()
替换为std::make_pair
,std::pair::pair
,然后将其转发到std::shared_prtr<Foo>
,std::pair::pair
由 move-ctor 创建为元素std::shared_prtr<Foo>
。最后,second
的r值也被重载:
std::map::insert
再次出现
// since C++11
template< class P >
std::pair<iterator,bool> insert( P&& value );
中的 move-ctor 。总之,我希望
std::shared_prtr<Foo>
调用my_map.insert(make_pair("key", bar()));
的move-ctor 两次。
我的答案
由于在这两种情况下都根本没有std::shared_ptr<Foo>
的副本,并且调用了几乎相同数量的移动指针,所以它们的性能几乎相同。
insert vs emplace vs operator[] in c++ map
Is c++11 operator[] equivalent to emplace on map insertion?
在C ++ 11及更高版本中,std::shared_ptr<Foo>
的左右参数都是Scott Meyers所说的universal reference,也就是说,它们既可以接受l值又可以接受r值:>
std::make_pair
因此,如果我们将l值传递给// until C++11
template< class T1, class T2 >
std::pair<T1,T2> make_pair( T1 t, T2 u );
// since C++11
template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );
,那么作为通用引用的结果,将调用std::make_pair
的copy-ctor。
std::shared_ptr<Foo>
始终构造std::map::emplace
的新元素,即使该键已经存在。
如果密钥已经存在,则这些新实例将被销毁。
但是从C ++ 17开始,我们得到了
my_map
如果密钥template <class... Args>
pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);
已存在于容器中,则此函数不会构造args
。
尽管我们已经知道当前问题中没有重复的密钥,但是如果我们不知道是否存在重复的密钥,则首选k
。
Is there any reason to use std::map::emplace() instead of try_emplace() in C++1z?