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

StackOverflow https://stackoverflow.com/questions/21438244

  •  04-10-2022
  •  | 
  •  

Domanda

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?

È stato utile?

Soluzione 2

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.

Altri suggerimenti

By making A moveable, but not copyable, and then moving the object to the emplacement function.

In C++03, you can use Boost.Move to make A moveable. Example:

#include <boost/container/map.hpp>
#include <boost/move/move.hpp>
#include <cassert>

class A { 
    BOOST_MOVABLE_BUT_NOT_COPYABLE(A)
public:
    explicit A() {}; 
    explicit A(bool b) {}; 
    A(BOOST_RV_REF(A)) {}
    A& operator=(BOOST_RV_REF(A)) { return * this; }

private:
    // Non-copyable
//    A(const A&);
//    const A& operator= (const A&);
};


int main()
{
    boost::container::map<int, A> myMap;
    myMap.emplace(1, true); // Works

    A a;
    myMap.emplace(1, boost::move (a));
//  myMap.emplace(1);       // Fails

}

piecewise_construct support was added recently for Boost 1.62. So the following code:

#include <boost/container/map.hpp>
#include <boost/tuple/tuple.hpp>

// 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&);
};

int main()
{
   using namespace boost::container;
   map<int, A> myMap;

   myMap.emplace(piecewise_construct, boost::tuple<int>(1), boost::tuple<>());

   return 0;
}

should work in C++03 and C++11+ compilers.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top