开发者

Returning a unique_ptr from a class method C++0x

开发者 https://www.devze.com 2023-01-17 02:19 出处:网络
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)

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_ptrs instead of unique_ptrs? This will make returning a value much safer. The whole point of unique_ptr is that it is unique (i.e. not shared).

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号