在map中使用std::unique_ptr作为键

7
我将使用Visual Studio 2012进行翻译。我有一张地图,其外观如下:
std::map<std::string,std::map<std::unique_ptr<sf::Sound>,std::unique_ptr<sf::SoundBuffer>>> listSoundContainer;

我正在尝试像这样插入数据:

std::unique_ptr<sf::SoundBuffer> soundBuffer(new sf::SoundBuffer());
if (soundBuffer->loadFromFile("assets/sound/" + _fileName) != false)
{
    std::unique_ptr<sf::Sound> sound(new sf::Sound(*soundBuffer));
    typedef std::map<std::unique_ptr<sf::Sound>, std::unique_ptr<sf::SoundBuffer>> innerMap;
    listSoundContainer[_fileName].insert(innerMap::value_type(std::move(sound), std::move(soundBuffer)));               
}

我在编译时遇到了以下错误:

microsoft visual studio 11.0\vc\include\utility(182): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>' 1> with 1>
[ 1> _Ty=sf::Sound 1> ] 1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\memory(1447) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr' 1> with 1> [ 1> _Ty=sf::Sound 1> ] 1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(617) : see reference to function template instantiation 'std::pair<_Ty1,_Ty2>::pair(std::pair<_Ty1,_Ty2> &&,void **)' being compiled 1> with 1> [ 1>
_Ty1=const std::unique_ptr, 1> _Ty2=std::unique_ptr, 1> _Kty=std::unique_ptr, 1> _Ty=std::unique_ptr 1> ]

我也尝试使用make_pair插入数据,但是出现了相同的问题。我错过了什么?我已经试图解决这个问题两个小时了,但是还是无法理解。

实际上,我可以通过不使用智能指针来解决这个问题:

sf::SoundBuffer* soundbuffer = new sf::SoundBuffer();
soundbuffer->loadFromFile(_file);
sf::Sound* sound = new sf::Sound(*soundbuffer);
typedef std::map<sf::SoundBuffer*, sf::Sound*> mapType;
listSound[_file].insert(mapType::value_type(soundbuffer, sound));

1
我认为另一个问题可能是你正在使用的 map::value_type。文档说它将键值转换为常量。 - meneldal
使用unique_ptr作为map的键类型非常奇怪,甚至是不可能的,因为Meneldal所述的原因。即使您设法让代码编译通过,您如何找到具有匹配键的内容呢?毕竟,unique_ptr是唯一的。即使您设法以某种方式构造另一个管理相同原始指针的unique_ptr,一旦您完成了find或使用operator[]进行索引,第二个unique_ptr将被销毁,从而删除它所管理的指针。您需要想出其他数据结构。 - Praetorian
Praetorian:我有一个多态键,不想将其存储为shared_ptr。比较将转发到键的operator<()。 - chila
2个回答

2

请看std::map的模板定义:

template<
    class Key,
    class T,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<std::pair<const Key, T> >
> class map;

现在我们来看一下如何尝试实例化它:

std::map<
    std::string, 
    std::map<
        std::unique_ptr<sf::Sound>, 
        std::unique_ptr<sf::SoundBuffer>
    >
> 
listSoundContainer

这里的问题是std::unique_ptr<sf::Sound>无法作为键。 您似乎尝试创建一些类似于std::pair<std::unique_ptr<sf::Sound>, std::unique_ptr<sf::SoundBuffer>>的列表。 我建议改用以下内容:
std::map<
    std::string, 
    std::list<
        std::pair<
            std::unique_ptr<sf::Sound>, 
            std::unique_ptr<sf::SoundBuffer>
        >
    >
> 
listSoundContainer

正如你所看到的,soundbuffer 会将文件读入内存,而 sound 类则几乎播放音频文件。我可以通过不使用智能指针来实现这一点。我已经编辑了我的帖子,请查看无智能指针的解决方案。 - user2946316
我理解您想使用一些 std::unique_ptr,但如果您在它们上调用 std::move,那么以后甚至无法访问这些值。除非您只是通过迭代器遍历所有内容,否则需要密钥才能访问这些值。 - meneldal
我可以始终通过listSoundContainer[_fileName].begin()->first访问它。我试图将一个文件加载到内存中,并在程序打开的同时使用它。例如,loadSound("bam.wav");应该流式传输文件并返回一个声音类对象,以便我可以播放它。如果我想再次使用相同的文件,则调用相同的函数,但这次它从列表中返回值,而不是再次读入内存。 - user2946316
1
我的意思是,如果你实际上不需要第二个映射的键,只需将其替换为一个 std::pair<std::unique_ptr<sf::Sound>, std::unique_ptr<sf::SoundBuffer>> 列表。 - meneldal
老兄,非常感谢。不知道为什么我没想到。爱你哟。 - user2946316
没有问题,STL 在你不确定自己真正需要什么的时候可能会让人感到相当困惑。特别是当理论上最适合你的应用的类型实际上不那么高效时(即使你只是按顺序读取它,列表可能比向量慢)。 - meneldal

-1

智能指针不应与STL容器结合使用。

背景是智能指针的行为与STL容器的预期不符。例如,STL期望复制操作的源对象保持不变。但智能指针并非如此。这可能导致一些奇怪的效果,正如您所经历的那样...

编辑:我的回答并不完全正确。自从C++11以来,可以使用智能指针(例如unique_ptr)与STL容器。


5
这个答案在C++11中是不正确的。unique_ptr和shared_ptr有特定的专业化,使它们可以用于std容器中。 - Richard Hodges

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