简体   繁体   中英

How I can use emplace for unordered_set that holds shared_ptr to the object?

Say I have an object:

struct Foo {
    Foo(const std::string& str1, const std::string& str1) 
        : mStr1(str1), mStr2(str2)
    {}

    std::string mStr1;
    std::string mStr2;
};

And set

typedef std::unordered_set<std::shared_ptr<Foo> , Hash, Compare> Set;

I have custom hasher and compare. But when I say:

Set set;
set.emplace(str1, str2);

I receive compile error, because the constructor of Foo is obviously not a constructor of std::shared_ptr<Foo> . What I would like is when emplace needs to construct a pointer to use std::make_shared<Foo>(str1, str2)

It seems that I also need a custom allocator for that, but I did not manage to implement one that satisfy the compiler.

My question is: Is what I want possible. If yes, how - is the allocator the right way to go. If yes, can you point me to an example.

Use set.insert(std::make_shared<Foo>(str1, str2)); . emplace is generally not a gain for container with unique keys when duplicate keys are an issue, because of the way it operates:

  • It must construct the object first before it can compare it to existing keys in the container to determine if it can be inserted.
  • Once the object is constructed, it cannot be copied or moved, because the object is not required to be copyable or movable. Moreover, there's no reliable way for emplace to detect when it can copy or move something, because there are plenty of types for which is_copy_constructible returns true but cannot be actually copied.
  • Object construction can only happen once, since the constructor may move from the arguments or have other side effects.

A typical implementation of emplace thus always allocates memory for the node up-front, constructs the object inside that memory, compares it with existing elements in the container, and then either links it in, or destroys it and deallocates the memory.

insert , on the other hand, has the key readily available. It can therefore first decide whether the node should be inserted, and only allocate memory if it should be.

In theory, implementations might special-case emplace for the "one argument with the same type as the element type" case. But I know of no implementation that actually does this.

I receive compile error, because the constructor of Foo is obviously not a constructor of std::shared_ptr. What I would like is when emplace needs to construct a pointer to use std::make_shared(str1, str2)

emplace is implemented as a function that uses perfect forwarding to invoke the constructor of the contained element (in this case shared_ptr). The contained element's constructor accepts a pointer to Foo, therefore you should be able to do this (just like you would construct a shared_ptr<Foo> object):

set.emplace(new Foo("x", "y")); //or
set.emplace(new Foo(str1, str2)); 

It seems that I also need a custom allocator for that, but I did not manage to implement one that satisfy the compiler.

A custom allocator is a total overkill if all you want to do is add a shared_ptr in the most efficient way (by invoking forwarding constructor on some pre-allocate element), or I'm totally misunderstanding your question. You would typically use the allocator if you don't want the element to be constructed using the default allocator (which use operator new). In this case, shared_ptr itself will be the element that will be constructed on the heap. You would only use an allocator if you are concerned that heap allocations are for some reason inefficient for your purposes (eg if you allocate millions of small objects).

Note (as commented by @Yakk) that, in this case it is possible that the instantiation of shared_ptr may throw (I can only think of bad_alloc as possibility), in which case the pointer passed to emplace would cause a leak. For this reason I too think std::make_shared would be a better option (as mentioned in another answer).

You can just use std::make_shared directly in the argument list of emplace .

set.emplace(std::make_shared<Foo>(str1, str2));

No custom allocator required.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM