C++ 通过移动有效地将元组插入到容器中
我是一个C++ 通过移动有效地将元组插入到容器中,c++,c++11,move-semantics,emplace,C++,C++11,Move Semantics,Emplace,我是一个move语义学初学者。这个代码是: template <typename... Args> void foo(const Args & ... args){ map<tuple<Args...>, int> cache; auto result = cache.emplace(move(make_tuple(args ...)),1); //... } 模板 void foo(常量参数和…参数){ 地图缓存;
move
语义学初学者。这个代码是:
template <typename... Args>
void foo(const Args & ... args){
map<tuple<Args...>, int> cache;
auto result = cache.emplace(move(make_tuple(args ...)),1);
//...
}
模板
void foo(常量参数和…参数){
地图缓存;
自动结果=cache.emplace(移动(生成元组(args…),1);
//...
}
效率高于:
template <typename... Args>
void foo(const Args & ... args){
map<tuple<Args...>, int> cache;
tuple<Args...> t(args...);
auto result = cache.insert(make_pair(t,1));
//...
}
模板
void foo(常量参数和…参数){
地图缓存;
元组t(args…);
自动结果=cache.insert(make_pair(t,1));
//...
}
尤其是当args
包含一些大对象时
同样的问题,但是使用了std::vector
(因此不需要make\u pair
或make\u tuple
)模板
void foo(常量参数和…参数){
地图缓存;
自动结果=cache.emplace(移动(生成元组(args…),1);
//...
}
这段代码应该更快<代码>安放执行就地构建(完美转发)。这应保证最低数量的构造和副本。但是,如果您对它们进行基准测试,这并没有什么坏处
通常,在可能的情况下使用emplace。它应该总是一个更好的选择。首先:
auto result = cache.emplace(move(make_tuple(args ...)),1);
vs
没什么区别make_tuple(args…
是一个临时值,因此作为右值引用传递。这一举动没有增加任何东西
会有不同的事情发生
tuple<Args...> t(args...);
auto result = cache.emplace(t, 1);
如果将右值引用传递给
foo()
,则转发(args).
将其作为右值引用转发,从而导致移动而不是复制。如果您使用左值引用调用foo()
,它将作为左值转发。因为这是用于记忆,所以这两个选项都不是一个好主意
对于唯一的密钥容器,放置
和插入
(除非插入
传递了一个值类型
-即,对
)可以无条件地分配内存并首先构造密钥-值对,然后如果密钥已经存在,则销毁该对并释放内存;如果您的密钥已经存在,那么这显然是非常昂贵的。(他们需要这样做,因为在一般情况下,您必须先构造密钥,然后才能检查它是否存在,并且必须直接在其最终位置构造密钥。)
但是,您还希望避免不必要地复制密钥,因此插入值_type
是不好的-其中的密钥是const限定的,因此无法从中移动
最后,您还希望避免额外的查找。虽然没有内存分配那么昂贵,但保存起来还是不错的
因此,我们需要首先查找密钥,并且仅在密钥不在地图中时调用emplace
。在C++11中,只允许同构查找,因此必须创建一个args…
副本
map<tuple<Args...>, int> cache;
auto key = std::make_tuple(args...);
auto it = cache.lower_bound(key); // *it is the first element whose key is
// not less than 'key'
if(it != cache.end() && it->first == key) {
// key already in the map, do what you have to do
}
else {
// add new entry, using 'it' as hint and moving 'key' into container.
cache.emplace_hint(it, std::move(key), /* value */);
}
地图缓存;
自动键=标准::生成元组(args;
自动it=缓存。下限(键);//*它是其键为的第一个元素
//不少于“关键”
if(it!=cache.end()&&it->first==key){
//已在地图中键入,请执行您必须执行的操作
}
否则{
//添加新条目,使用“it”作为提示,并将“key”移动到容器中。
cache.emplace_hint(it,std::move(key),/*value*/);
}
在C++14中,您可以执行异构查找,这意味着您可以在实际需要时保存副本:
map<tuple<Args...>, int, less<>> cache; // "diamond functor" enables hetergeneous lookup
auto key = std::tie(args...); // this is a tuple<const Args&...> - a tuple of references!
auto it = cache.lower_bound(key); // *it is the first element whose key is
// not less than 'key'
if(it != cache.end() && it->first == key) {
// key already in the map, do what you have to do
}
else {
// add new entry, copying args...
cache.emplace_hint(it, key, /* value */);
}
映射缓存;//“菱形函子”启用非同步查找
自动关键点=标准::连接(参数…)//这是一个元组-一个引用元组!
自动it=缓存。下限(键);//*它是其键为的第一个元素
//不少于“关键”
if(it!=cache.end()&&it->first==key){
//已在地图中键入,请执行您必须执行的操作
}
否则{
//添加新条目,复制参数。。。
cache.emplace_hint(it,key,/*value*/);
}
顺便说一句,将大对象作为映射键的元组不是最佳选项,那么您可以提出一个吗?:)我应该怎么建议,因为我根本不知道你的问题是什么。你显然是对的:这是记忆编辑器的一部分,其中args
是输入(为了简单起见,我没有使用值类型,它是记忆函数的返回类型)好的,第一个可能的改进是使用unordered\u map
,因为键顺序是不相关的谢谢,我很高兴我终于开始理解move
技巧:)顺便说一句,原地构建是因为位置而不是因为移动,但是move(…)
是没有用的?@justHelloWorld老实说。。我不知道。我不确定完美的转发是否会占用maketuple并执行到位。如果是这样的话,那么采取行动是没有用的,从另一方面来说,这是很重要的。你可能不会接受这个答案,因为有一个更了解情况的人会给你一个更好的答案。我很感激你的诚实:)至少,不要再说了,const
@T.C.是个复制/粘贴的疏忽,谢谢。但是你所说的“至少”是什么意思呢?谢谢你的回答,我真的很感激。第一个问题:我对args使用const,因为它是管理左值引用的好习惯。那么,如果我们删除它,会有什么变化呢(因为您的注释“there中的键是const-qualified的,因此无法从中移动”)。第二个问题:没有必要保留map保证的键顺序,因此无序的_映射可能是首选。但是google::dense_hash_map的性能会更好,所以问题是:使用此结构或任何第三方哈希结构都可以进行异构查找?@justHelloWorld 1)问题是map
的值类型(对),而不是参数;你对此无能为力。2) 无序的_映射不支持异构查找;我不知道第三方散列结构-你必须检查他们的文档。我认为你的代码不起作用,因为你正在比较元组
(sokey
)和tuple`(soit->first
),但请回答我在这个问题上提出的问题。@justHelloWorldstd
template <typename... Args>
void foo(Args && ... args){
map<tuple<Args...>, int> cache;
auto result = cache.emplace(make_tuple(forward<Args>(args)...)),1);
//...
}
map<tuple<Args...>, int> cache;
auto key = std::make_tuple(args...);
auto it = cache.lower_bound(key); // *it is the first element whose key is
// not less than 'key'
if(it != cache.end() && it->first == key) {
// key already in the map, do what you have to do
}
else {
// add new entry, using 'it' as hint and moving 'key' into container.
cache.emplace_hint(it, std::move(key), /* value */);
}
map<tuple<Args...>, int, less<>> cache; // "diamond functor" enables hetergeneous lookup
auto key = std::tie(args...); // this is a tuple<const Args&...> - a tuple of references!
auto it = cache.lower_bound(key); // *it is the first element whose key is
// not less than 'key'
if(it != cache.end() && it->first == key) {
// key already in the map, do what you have to do
}
else {
// add new entry, copying args...
cache.emplace_hint(it, key, /* value */);
}