Returning a unique_ptr from a class method C++0x
If my class SomeType has a method that returns a element from the map (using the key) say
std::unique_ptr<OtherType> get_othertype(std::string name)
{
return otMap.find(name);
}
that would enure the caller would recieve a pointer to the one in the map rather than a copy? Is it ok to do this, or would it try and call the copy constructor (and fail as it has been removed) because it is being returned?
Assuming I must use unique_ptr as my map items.
UPDATE::
After trying to implement the code, it seems that unique_ptr and std:map/:pair dont work together in gcc 4.4.4, pair just didnt like unique_ptr as a type parameter. (see Can't create map of MoveConstructibles).
开发者_C百科I changed the ptr to std::shared_ptr and it all worked.
I suppose I can use the same code with the shared pointer?
The model of unique_ptr
is transfer of ownership. If you return a unique_ptr
to an object from a function, then no other unique_ptr
in the system can possibly refer to the same object.
Is that what you want? I highly doubt it. Of course, you could simply return a raw pointer:
OtherType* get_othertype(const std::string& name)
{
return otMap.find(name)->second.get();
}
Thus, the client has access to the object, but the map still owns it.
The above solution is rather brittle in case there is no entry found under the name. A better solution would be to either throw an exception or return a null pointer in that case:
#include <stdexcept>
OtherType* get_othertype(const std::string& name)
{
auto it = otMap.find(name);
if (it == otMap.end()) throw std::invalid_argument("entry not found");
return it->second.get();
}
OtherType* get_othertype(const std::string& name)
{
auto it = otMap.find(name);
return (it == otMap.end()) ? 0 : it->second.get();
}
And just for completeness, here is Anthony's suggestion of returning a reference:
OtherType& get_othertype(const std::string& name)
{
auto it = otMap.find(name);
if (it == otMap.end()) throw std::invalid_argument("entry not found");
return *(it->second);
}
And here is how you return a reference to the unique_ptr
inside the map, but let's make that a reference to const, so the client does not accidentally modify the original:
unique_ptr<OtherType> const& get_othertype(const std::string& name)
{
auto it = otMap.find(name);
if (it == otMap.end()) throw std::invalid_argument("entry not found");
return it->second;
}
What is the type of otMap
?
If otMap.find(name)
returns a std::unique_ptr<OtherType>
as an rvalue then this will work fine. However, ownership of the pointed-to value has now been transferred to the returned pointer, so the value will no longer be in the map. This would imply you were using a custom map type rather than std::map<>
.
If you want to be able to have the value in the map and return a pointer to it, then you need to use std::shared_ptr<OtherType>
both as the map value type and the return type of get_othertype()
.
std::map<std::string,std::shared_ptr<OtherType>> otMap;
std::shared_ptr<OtherType> get_othertype(std::string name)
{
auto found=otMap.find(name);
if(found!=otMap.end())
return found->second;
return std::shared_ptr<OtherType>();
}
otMap.find will return an rvalue and thus this rvalue will be moved, if not RVO'd. But, of course, now your map doesn't have that particular object in. Also, last time I checked, find returns an iterator, not the value type.
Would you consider changing the map to hold shared_ptr
s instead of unique_ptr
s? This will make returning a value much safer. The whole point of unique_ptr
is that it is unique (i.e. not shared).
精彩评论