Search code examples
c++dictionaryboostemplace

How can a default-constructed object of a non-copyable class be emplaced into a boost::container::map?


Consider the folllowing C++03 code (I have to make the code compatible with pre-C++11 compilers):

// This class belongs to a third-party SDK and cannot be touched
class A {
public:
    explicit A();
    explicit A(bool b);
private:
    // Non-copyable
    A(const A&);
    const A& operator= (const A&);
}

boost::container::map<int, A> myMap;

Using a Boost map here because it allows emplacing even in C++03. The problem is that I can perflectly emplace-construct into the map if using the one-argument constructor, but I don't know how to default-construct the object, as this code illustrates:

myMap.emplace(1, true); // Works
myMap.emplace(1);       // Fails

The second call fails because it's taken as a call to the emplace(std::pair...) overload so it seems there is no way to "default-emplace".

Is there any way to achieve what I want?


Solution

  • Out of interest I played with this

    As an important datapoint, I knew that std::map (in C++11) supports piecewise construction of it's value pairs:

    std::map<int, A> stdMap;
    stdMap.emplace(std::piecewise_construct, 
         std::forward_as_tuple(1), 
         std::forward_as_tuple());
    

    would therefore invoke the contructor you're after. However, somehow, the same doesn't immediately work for Boost's map. Hmmm.

    However, this piqued my interest: boost uses std::pair<const K, V> as the value type?!

    boost::container::map<int, A>::value_type p { 
           std::piecewise_construct, 
           std::forward_as_tuple(1), 
           std::forward_as_tuple(true) 
    };
    

    works no problem. And I can also verify that this typedef is in fact the type stored:

    static_assert(std::is_same<decltype(p), std::remove_reference<decltype(*myMap.begin())>::type>::value, "Nonstandard pair");
    

    So, it's beginning to look like a bug in the forwarding via the interal tree implementation when it uses the allocator::construct call.