Why Does the C++ Map Type Argument Require an Empty Constructor When Using []

Why does the C++ map type argument require an empty constructor when using []?

This issue comes with operator[]. Quote from SGI documentation:

data_type& operator[](const key_type& k) - Returns a reference to the object
that is associated with a particular
key. If the map does not already
contain such an object, operator[]
inserts the default object
data_type().

If you don't have default constructor you can use insert/find functions.
Following example works fine:

myMap.insert( std::map< int, MyClass >::value_type ( 1, MyClass(1) ) );
myMap.find( 1 )->second;

Why does std::map::operator[] assignment require an argumentless constructor?

Why is [a constructor for B without any arguments] needed?

This is because std::map::operator[] required the mapped_type (i.e. in your case B) to be default constructable.

  1. Inserts value_type(key, T()) if the key does not exist. This function is equivalent to return insert(std::make_pair(key, T())).first->second;
    • key_type must meet the requirements of CopyConstructible.
    • mapped_type must meet the requirements of CopyConstructible and DefaultConstructible.
      If an insertion is performed, the mapped value is value-initialized (default-constructed for class types, zero-initialized otherwise) and a reference to it is returned.

When you provide a user defined constructor (i.e. B(int b)), the compiler will not generate a default constructor automatically, and thereby A can not be default construable.

If some user-declared constructors are present, the user may still force the automatic generation of a default constructor by the compiler that would be implicitly-declared otherwise with the keyword default.

Hence, the above error!



why does using insert instead of operator[] not need that constructor?

Because std::map::insert relies on the value_type (i.e. std::pair<const Key, T>). For your ab this is std::pair<const int, B>. From the cppreference.com the function overloads:

1-3) Inserts value. The overload (2) is equivalent to emplace(std::forward<P>(value)) and only participates in overload resolution if std::is_constructible<value_type, P&&>::value == true.

4-6) Inserts value in the position as close as possible, just prior(since C++11), to hint. The overload (5) is equivalent to emplace_hint(hint, std::forward<P>(value)) and only participates in overload resolution if std::is_constructible<value_type, P&&>::value == true.

So as long as B is constructable the std::pair<const int, B> also can be constructed and std::map::insert can be work.

C++17 map emplace without default constructor (private default constructor)

Are you sure that the line giving the error is not this one:

   return database_instances[session_name];

rather than the call to emplace?

That line will attempt to default construct a value of DB in the case where the key does not exist in the map.

Adding an object to std::map doesn't work unless an empty (no args) constructor for the class' object exists

operator[] requires that the type is DefaultConstructible, if the key does not exist.

On line params[name] = data;

  1. operator[] creates an element using the default constructor
  2. operator[] returns a reference to the element
  3. data is assigned to the reference, using the copy constructor

In your case, the step 1 fails because there is no default constructor.

C++17 adds insert_or_assign(), which does not require the type to be DefaultConstructible.

Using std::mapK,V where V has no usable default constructor

You can't make the compiler differentiate between the two uses of operator[], because they are the same thing. Operator[] returns a reference, so the assignment version is just assigning to that reference.

Personally, I never use operator[] for maps for anything but quick and dirty demo code. Use insert() and find() instead. Note that the make_pair() function makes insert easier to use:

m.insert( make_pair( k, v ) );

In C++11, you can also do

m.emplace( k, v );
m.emplace( piecewise_construct, make_tuple(k), make_tuple(the_constructor_arg_of_v) );

even if the copy/move constructor is not supplied.

How to emplace object with no-argument constructor into std::map?

The element type of std::map<K, V> is actually std::pair<K, V>, so when you are emplacing into a map, the arguments will be forwarded to the constructor of std::pair. That's why you can't pass just the key: std::pair<K, V> can't be constructed from a single argument (unless it's another pair of the same type.) You can pass zero arguments, but then the key will be value-initialized, which is probably not what you want.

In most cases, moving values will be cheap (and keys will be small and copyable) and you should really just do something like this:

M.emplace(k, V{});

where V is the mapped type. It will be value-initialized and moved into the container. (The move might even be elided; I'm not sure.)

If you can't move, and you really need the V to be constructed in-place, you have to use the piecewise construction constructor...

M.emplace(std::piecewise_construct, std::make_tuple(k), std::make_tuple());

This causes std::pair to construct the first element using k and the second element using zero arguments (value-initialization).



Related Topics



Leave a reply



Submit