9

I am using Visual Studio 2012. I have a map that looks like this:

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

I'm trying to insert data like this:

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))); } 

and im getting the following error at compile time:

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> ]

I have also tried to insert data using make_pair with the same problem. What am I missing? Ive been trying to solve this problem for 2 hours now and can't get my head around it.

I can actually solve this problem by NOT using smart pointers:

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)); 
3
  • 1
    I think maybe another problem might be that map::value_type you are using. Documentation says it transforms the Key value in a constant Commented Apr 27, 2015 at 4:50
  • Using a unique_ptr as a map's key type is very strange, if not impossible because of the reason stated above by meneldal. Even if you manage to get the code to compile, how would you ever find something with a matching key? unique_ptrs are after all, unique. Even if you managed to somehow construct another unique_ptr managing the same raw pointer, as soon as you're done finding or indexing using operator[], the second unique_ptr would be destroyed, thereby deleting the pointer it manages, You need to come up with some other data structure. Commented Apr 27, 2015 at 5:07
  • Praetorian: I have a polymorphic key and I don't want to store it as a shared_ptr. The comparison forwards to the key's operator<(). Commented Oct 11, 2019 at 18:52

3 Answers 3

3

Look at the template definition for std::map:

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

And now lets look at how you try to instantiate it:

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

The problem here is that a std::unique_ptr<sf::Sound> cannot act as a key.

What you seem trying to do is to make some kind of list of std::pair<std::unique_ptr<sf::Sound>, std::unique_ptr<sf::SoundBuffer>>

I would suggest using this instead:

std::map< std::string, std::list< std::pair< std::unique_ptr<sf::Sound>, std::unique_ptr<sf::SoundBuffer> > > > listSoundContainer 
Sign up to request clarification or add additional context in comments.

6 Comments

well as you can see, soundbuffer reads a file into memory and the sound class pretty much plays the audio file. I can achiev this by NOT using smart pointers. I have edited my post, look at the solution without smart pointer
I understand you want to use some std::unique_ptr but if you call std::move on them you can't even access the values later. You need the key to access the values unless you're just going through everything with an iterator.
Well i can always access it with listSoundContainer[_fileName].begin()->first. Im trying to load 1 single file into the memory and use it as long as the program is open. For example a loadSound("bam.wav"); should stream the file once and return a sound class obj so i can play it. If i want to use the same file again, i call the same function but this time it returns the value from the list instead of reading it into memory once again.
I mean if you don't actually need a key for the second map just replace it with a list of std::pair<std::unique_ptr<sf::Sound>, std::unique_ptr<sf::SoundBuffer>>
dude, thanks alot. Don't know why I didn't think about it. Much love
|
0

In C++20 it's now possible to use unique_ptr keys within a map, using Heterogenous Lookup.

In fact, being able to use unique_ptr keys was one of the motivations for introducing heterogenous lookup.

Working example: https://godbolt.org/z/PTjEjb7Go

Comments

-2

Smart pointers should not be used in combination with STL containers.

The background is that smart pointers do not behave as expected by the STL-containers. For instance, the STL expects source objects of a copy operation to remain unchanged. This is not the case with smart pointers. This can lead to strange effects as you are experiencing...

EDIT: My answer was not fully correct. Since C++11 it is possible to use smart pointers, e.g. unique_ptr, with STL-containers.

1 Comment

This answer is incorrect as of c++11. unique_ptr and shared_ptr have specific specialisations that allow them to be used in std containters