允许从 std::map 的键中窃取资源吗?

phi*_*inz 15 c++ move-semantics

在 C++ 中,是否可以从我以后不再需要的地图中窃取资源?更准确地说,假设我有一个std::mapwithstd::string键,我想通过map使用std::move. 请注意,对键的这种写访问会破坏 的内部数据结构(键的顺序),map但之后我不会使用它。

问题:我可以在没有任何问题的情况下执行此操作,还是会导致意外错误,例如在析构函数中出现意外错误,map因为我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;
}
Run Code Online (Sandbox Code Playgroud)

它产生输出:

key before stealing: aLongString
key after stealing: 
key before stealing: anotherLongString
key after stealing: 
Run Code Online (Sandbox Code Playgroud)

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

dru*_*nly 17

您正在执行未定义的行为,const_cast用于修改const变量。不要那样做。这const是因为地图是按它们的键排序的。因此,就地修改密钥打破了构建地图的基本假设。

您永远不应该使用从变量中const_cast删除修改该变量。const

话虽如此,C++17 可以解决您的问题:std::mapextract功能:

#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())));
}
Run Code Online (Sandbox Code Playgroud)

不要using namespace std;。:)

  • 映射的键被创建为“const”。改变“const”对象是即时的 UB,无论之后是否有任何东西实际访问它们。 (2认同)
  • @phinz正如你所见,[在cppreference上](https://en.cppreference.com/w/cpp/container/map/extract)当使用迭代器作为参数时,`extract`具有摊销恒定的复杂性。*一些*开销是不可避免的,但它可能不会大到足以产生影响。如果您有未涵盖的特殊要求,那么您将需要实现自己的“地图”来满足这些要求。“std”容器适用于常见的通用应用程序。它们并未针对特定用例进行优化。 (2认同)