从std::map'窃取资源;允许带钥匙吗? 在C++中,从一个我不再需要的地图中盗取资源是可以的吗?更准确地说,假设我有一个带有std::string键的std::map,我想通过使用std::move窃取maps键的资源,从中构造一个向量。请注意,对键的这种写访问会破坏映射的内部数据结构(键的顺序),但我以后不会使用它

从std::map'窃取资源;允许带钥匙吗? 在C++中,从一个我不再需要的地图中盗取资源是可以的吗?更准确地说,假设我有一个带有std::string键的std::map,我想通过使用std::move窃取maps键的资源,从中构造一个向量。请注意,对键的这种写访问会破坏映射的内部数据结构(键的顺序),但我以后不会使用它,c++,move-semantics,C++,Move Semantics,问题:我是否可以毫无问题地执行此操作,或者这是否会导致意外的错误,例如在映射的析构函数中,因为我以std::map不适用的方式访问它 以下是一个示例程序: #include<map> #include<string> #include<vector> #include<iostream> using namespace std; int main(int argc, char *argv[]) { std::vector<std::p

问题:我是否可以毫无问题地执行此操作,或者这是否会导致意外的错误,例如在
映射
的析构函数中,因为我以
std::map
不适用的方式访问它

以下是一个示例程序:

#include<map>
#include<string>
#include<vector>
#include<iostream>
using namespace std;
int main(int argc, char *argv[])
{
    std::vector<std::pair<std::string,double>> v;
    { // new scope to make clear that m is not needed 
      // after the resources were stolen
        std::map<std::string,double> m;
        m["aLongString"]=1.0;
        m["anotherLongString"]=2.0;
        //
        // now steal resources
        for (auto &p : m) {
            // according to my IDE, p has type 
            // std::pair<const class std::__cxx11::basic_string<char>, double>&
            cout<<"key before stealing: "<<p.first<<endl;
            v.emplace_back(make_pair(std::move(const_cast<string&>(p.first)),p.second));
            cout<<"key after stealing: "<<p.first<<endl;
        }
    }
    // now use v
    return 0;
}

编辑:我想对大型地图的整个内容执行此操作,并通过此资源窃取保存动态分配。

您正在执行未定义的行为,使用
const\u cast
修改
const
变量。不要那样做。之所以它是
const
,是因为地图是按键排序的。因此,在适当的位置修改一个键打破了构建地图的基本假设

切勿使用
const\u cast
从变量中删除
const
,并修改该变量

也就是说,C++17为您的问题提供了解决方案:
std::map
extract
函数:

#include <map>
#include <string>
#include <vector>
#include <utility>

int main() {
  std::vector<std::pair<std::string, double>> v;
  std::map<std::string, double> m{{"aLongString", 1.0},
                                  {"anotherLongString", 2.0}};

  auto extracted_value = m.extract("aLongString");
  v.emplace_back(std::make_pair(std::move(extracted_value.key()),
                                std::move(extracted_value.mapped())));

  extracted_value = m.extract("anotherLongString");
  v.emplace_back(std::make_pair(std::move(extracted_value.key()),
                                std::move(extracted_value.mapped())));
}
#包括
#包括
#包括
#包括
int main(){
std::向量v;
映射m{{“aLongString”,1.0},
{“anotherLongString”,2.0};
自动提取的_值=m.extract(“aLongString”);
v、 向后放置(std::make_pair(std::move(extracted_value.key()),
std::move(提取的_value.mapped());
提取值=m.extract(“另一个长字符串”);
v、 向后放置(std::make_pair(std::move(extracted_value.key()),
std::move(提取的_value.mapped());
}

不要
使用名称空间std:)

编辑:这个答案是错误的。善意的评论指出了错误,但我不会删除它,因为它已在其他答案中引用。

@druckermanly回答了您的第一个问题,即强行更改
map
中的键会破坏
map
内部数据结构构建的有序性(红黑树)。但是使用
extract
方法是安全的,因为它做了两件事:将密钥移出地图,然后将其删除,因此它根本不会影响地图的有序性


你提出的另一个问题,关于解构时是否会引起麻烦,不是问题。当映射解构时,它将调用其每个元素的解构器(mapped_类型等),并且
move
方法确保在类被移动后解构类是安全的。所以别担心。简而言之,正是
move
的操作确保了安全地删除或重新为“moved”类分配一些新值。特别是对于
string
move
方法可以将其char指针设置为
nullptr
,因此它不会删除调用原始类的解构器时移动的实际数据


一条评论让我想起了我忽略的一点,基本上他是对的,但有一点我并不完全同意:
const\u cast
可能不是UB<代码>常量
只是编译器和我们之间的承诺。标注为
const
的对象仍然是对象,就其类型和二进制形式的表示而言,与未标注为
const
的对象相同。当
常量
被丢弃时,它的行为应该像是一个普通的可变类。关于
移动
,如果你想使用它,你必须传递一个
&
而不是
常量&
,因此我认为它不是UB,它只是违背了
常量
的承诺,将数据移走

我还做了两个实验,分别使用MSVC14.24.28314和CLANG9.0.0,得到了相同的结果

map-m;
m、 插入({“测试”,2});
m、 插入({“这应该在'test'字符串后面。”,3});
m、 插入({“并且应该在'test'字符串前面。”,1});
字符串let_me_USE_IT=std::move(const_cast(m.find(“test”)->first));

我不认为在这种情况下,
const_cast
和修改会导致未定义的行为,但请评论此论点是否正确

答案声称

换句话说,如果修改一个最初的常量对象,则得到UB,否则不会得到UB

因此,行
v.emplace_back(make_pair(std::move(const_cast(p.first)),p.second))字符串
对象
p.first
未创建为常量对象时,问题中的code>不会导致UB。现在请注意 州

提取节点会使提取元素的迭代器无效。提取的元素的指针和引用仍然有效,但在元素由节点句柄拥有时不能使用:如果元素插入到容器中,指针和引用将变得可用

因此,如果我
提取
p
相对应的
节点的句柄
p
继续存在于其存储位置。但是在提取之后,我被允许移动
p
资源,就像在代码中一样。这意味着
p
,因此
string
对象
p.first
最初不是作为const对象创建的

因此,我认为修改
map
的键不会导致UB和from,似乎
map
现在损坏的树结构(现在有多个相同的空字符串键)也不会在析构函数中引入问题。因此,问题中的代码在
#include <map>
#include <string>
#include <vector>
#include <utility>

int main() {
  std::vector<std::pair<std::string, double>> v;
  std::map<std::string, double> m{{"aLongString", 1.0},
                                  {"anotherLongString", 2.0}};

  auto extracted_value = m.extract("aLongString");
  v.emplace_back(std::make_pair(std::move(extracted_value.key()),
                                std::move(extracted_value.mapped())));

  extracted_value = m.extract("anotherLongString");
  v.emplace_back(std::make_pair(std::move(extracted_value.key()),
                                std::move(extracted_value.mapped())));
}
test
and this should be in front of the 'test' string. 1
 2
this should be behind the 'test' string. 3