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.
- Inserts
value_type(key, T())
if the key does not exist. This function is equivalent to returninsert(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 ofoperator[]
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 ifstd::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 ifstd::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;
operator[]
creates an element using the default constructoroperator[]
returns a reference to the elementdata
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
Is It Safe to Delete a Null Pointer
To_String Is Not a Member of Std, Says G++ (Mingw)
Why Doesn't a Simple "Hello World"-Style Program Compile With Turbo C++
How to Add Additional Libraries to Visual Studio Project
C++0X Has No Semaphores? How to Synchronize Threads
Significance of -Pthread Flag When Compiling
Recommended Way to Initialize Srand
Variable Length Array (Vla) in C++ Compilers
Copy a File in a Sane, Safe and Efficient Way
Sorting Zipped (Locked) Containers in C++ Using Boost or the Stl
Range Based Loop: Get Item by Value or Reference to Const