4

For containers such as std::map< std::string, std::unique_ptr< Foo >>, it looks like emplace() has yet to be implemented in stdc++ as of gcc 4.7.2.

Unfortunately, I can't store Foo directly by value as it is an abstract super-class.

As a simple, but inefficient, place-holder, I've just been using std::map< std::string, Foo* > in conjunction with a std::vector< std::unique_ptr< Foo >> for garbage collection.

Do you have a interim solution that is more efficient and more easily replaced once emplace() is available?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
kfmfe04
  • 14,936
  • 14
  • 74
  • 140

2 Answers2

11

What do you need emplace() for? Just move it in:

#include <iostream>
#include <map>
#include <memory>
#include <string>

struct Foo
{
    virtual ~Foo() = default;

    virtual std::string name() const = 0;
};

struct Bar : Foo
{
    std::string name() const { return "Bar"; }
};

int main()
{
    std::map<std::string, std::unique_ptr<Foo>> m;

    std::unique_ptr<Foo> p(new Bar());
    m.insert(std::make_pair("a", std::move(p)));

    std::cout << m["a"]->name() << std::endl;
}

In fact, you should not use emplace with unique_ptr's.

As noted in my comment there, I now consider the use of new in user code an error. It should be replaced with make_unique, so you know your resource cannot possibly leak:

// will be in std:: someday
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

int main()
{
    std::map<std::string, std::unique_ptr<Foo>> m;

    m.insert(std::make_pair("a", make_unique<Bar>()));

    std::cout << m["a"]->name() << std::endl;
}
Community
  • 1
  • 1
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • +1 for a good answer with useable code. Avoiding the use of `new` in user code is a valiant cause. However, suppose instead of constructing a new instance of Bar, I need to insert into the map a `foo->clone()` because I don't know what kind of `Foo` I have? How would you make `Foo* Bar::clone()` avoid calling `new`? Or is this ok, because it wouldn't be considered client code? – kfmfe04 Dec 09 '12 at 08:13
  • 1
    Coincidentally I just dealt with that today. Make your clone function return `std::unique_ptr` instead. – GManNickG Dec 09 '12 at 08:41
  • I know this is a bit old, but what if the user of the foo->clone() wants a shared_ptr? – Dalibor Frivaldsky Jun 02 '14 at 14:11
  • @DaliborFrivaldsky: You can construct a shared_ptr from a unique_ptr. – GManNickG Jun 03 '14 at 14:02
2

As a workaround you may use, boost containers which support most C++ 11 features even under a C++ 03 compiler and have same layout as std containers and then when you have then feature in you std you can just switch a name space!

BigBoss
  • 6,904
  • 2
  • 23
  • 38