std::ofstream的unordered_map

3

我想使用一个std::unordered_map<unsigned,std::ofstream>,但是失败了。现在我想知道这是不是根本不可能,或者是编译器问题,还是我只是没有弄对。问题在于插入另一个元素:

std::ofstream&get(unsigned key, std::unordered_map<unsigned,std::ofstream>&map)
{
  auto file = map.find(key);
  if(file==map.end()) {
    map.emplace(std::make_pair(key,std::ofstream{}));  // <-- trouble here
    file = map.find(key);
    assert(file!=map.end());
  }
  return file->second;
}

使用gcc(4.8.1)时会失败,因为std::ofstream不可复制。然而,它是可移动的,所以应该有一个解决方案,或者没有?(我想要插入的只是一个默认构造的std::ofstream)。我尝试添加一些std::move(),但那没有帮助。

我刚刚注意到上面的代码在clang 3.4中确实编译了。这不对吗?


2
这很可能是GCC及/或其标准库的问题。Clang及其libc++标准库在C++11兼容性方面一直比GCC/libstdc++更成熟。 - Some programmer dude
2个回答

3
你首先创建了一对对象,然后请求map再次构建这对对象,从而导致问题。
尝试:
map.emplace(key,std::ofstream{});

实际上,您所需要的只是

其实,你只需要

std::ofstream&get(unsigned key, std::unordered_map<unsigned,std::ofstream>&map)
{
  return map[key];
}

或者移除这个函数...使用map[key]

因为如果使用不存在的键,map将使用默认构造函数构造ofstream,所以它应该像你的代码一样工作。


同样的问题(在gcc 4.8.1中有问题,在clang 3.4中正常),即使使用std::move(std::ofstream{})也没有帮助。也许这是gcc的C++库的一个问题? - Walter
@Walter 实际上你根本不需要这个函数。 - Bryan Chen
太好了!我想,这不是线程安全的... 所以我需要一个互斥锁(然后仍然需要这个函数)。 - Walter

0

这与GCC编译器无关,其他编译器也应该是一样的,basic_fstream提供了相当简单的操作,此外文件流没有复制操作。

解决方案是,如果你想要两个名称引用同一个文件流,可以使用引用或指针,或者操作文件流缓冲区。

STL经常使用复制,上述使用emplacemake_pair的方式更方便地表示insert()m[k]的结果等同于(*(<map>.insert(make_pair(k,V{})).first)).second,其中V是映射类型,所以其中没有移动操作。

移动和复制之间微妙的差别在于,在复制之后,两个对象必须具有相同的值,而在移动之后,移动源不需要保留其原始值。移动可用于源对象将不再使用的情况下。它们特别适用于实现资源的移动概念。对于最有趣的情况,即容器,被移动的状态是“空的”。

一个对象在以下五种情况下会被复制或移动: • 作为赋值的源对象 • 作为对象初始化器 • 作为函数参数 • 作为函数返回值 • 作为异常 在所有情况下,都会应用复制或移动构造函数(除非可以进行优化)。

我认为在当前标准中很难适应这种情况,如果不是你所提到的情况,你为什么不使用 ofstream 的指针呢?像这样:

unordered_map<int, ofstream*> map1;
map1.emplace(1, new ofstream());

你有没有关于其他与move一起工作的编译器的想法?

不不不。map.insert(make_pair(k,V{}))在gcc的C++库中无法编译通过,但map[k]可以。所以它们并不等价。也许它们应该是等价的。map<int, ofstream*>正是我想要避免的。(顺便说一下,如果你真的想/必须使用指针,那么不要使用原始指针,而是使用map<int, unique_ptr<ofstream>> - Walter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接