Why Is a Default Constructor Required When Storing in a Map

Why is a default constructor required when storing in a map?

mymap["hello"] can attempt to create a value-initialized A, so a default constructor is required.

If you're using a type T as a map value (and plan to access value via operator[]), it needs to be default-constructible - i.e. you need a parameter-less (default) constructor. operator[] on a map will value-initialize the mapped value if a value with the key provided is not found.

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 a default constructor is needed using unordered_map and tuple?

visited_info[rect0] = info0;

well, what do you think this does? It's well-documented that the left-hand side evaluates to a reference to an item stored in the map. If that item wasn't there before, it is default constructed first.

You then use either copy- or move-assignment to update that default-constructed item from the right-hand-side of the expression.

If you want to avoid the default-construct-and-assign operation you're getting now, use emplace instead.


NB. One possible source of confusion is, for example, Python, where MyObj[1] might translate to a __getitem__ call, but MyObj[1]=1 to a __setitem__ call.

In C++, both the left-hand-side and right-hand-side expressions must evaluate to something, without knowing anything about the statement they're in. Hence, the left-hand-side evaluates to a reference which you can either read from or assign to - but the object needs to exist before you can take that reference.

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.

std::map calls default constructor on [], copy constructor on insert()

[] creates an object if it does not exist then returns a reference to it. At that time, no arguments are available.

= then assigns to this reference.

insert has no need to do that. It can simply construct in place using the pair you pass in.

With careful use, emplace can even do away with the copy or move ctor call.

Storing non copyable object with no default constructor in map (C++11)

What you're doing is:

map<string,Elem> m;
m["file1"] = // this default-constructs an Elem
// and returns a reference to it in the correct spot
move(Elem("file1")); // and THEN move-assigns it

What you need to do:

map<string, Elem> m;
m.emplace("file1", Elem("file1"));

or

m.insert(std::make_pair("file1", Elem("file1"));

or

m.emplace(std::piecewise_construct,
std::forward_as_tuple("file1"),
std::forward_as_tuple("file1"));

As for your design, the comments point out the issue that you're potentially leaking a stream. But this is actually a really easy fix. What you are expressing is unique ownership over a file, where that ownership is transferable. There's a type for that: unique_ptr!

class Elem {
std::unique_ptr<ifstream> file;

public:
Elem() = delete;
Elem(string name) : file(new ifstream(name,ios::in)) {}

Elem(Elem&& ) = default;
Elem& operator=(Elem&& ) = default;
~Elem() = default;

Elem(const Elem&) = delete;
Elem& operator=(const Elem&) = delete;
};

Using unique_ptr makes those default/delete lines not even necessary (except the default constructor one - since that's a requirement you're enforcing), which is even better (see Rule of Zero). I'm just illustrating them for clarity.

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.

No appropriate default constructor available

You're probably using the map's operator[] which does require the default constructor (if it didn't, how would it handle the case where the key doesn't exist in the map?). If you use insert instead you may be able to get away with not providing one (I can't recall if the standard requires a default constructor for all maps, or just when you use that operator).

Template Error: no appropriate default constructor available

std::map<>::operator[] requires a default constructor. This is because it first creates an entry in the map then does the operator= that you are calling.

If you absolutely want to use an std::map but do not want to provide a default constructor (perhaps it is illogical for your case?) you can use:

std::map<>::insert()

To explicitly insert the object into the code. This makes things a bit more complicated then because look ups must also use find.

I just noticed that Adam Rosenfield already posted this info in a comment but going ahead and leaving this as a separate answer.



Related Topics



Leave a reply



Submit