134

See also C++ standard list and default-constructible types

Not a major issue, just annoying as I don't want my class to ever be instantiated without the particular arguments.

#include <map>

struct MyClass
{
    MyClass(int t);
};

int main() {
    std::map<int, MyClass> myMap;
    myMap[14] = MyClass(42);
}

This gives me the following g++ error:

/usr/include/c++/4.3/bits/stl_map.h:419: error: no matching function for call to ‘MyClass()’

This compiles fine if I add a default constructor; I am certain it's not caused by incorrect syntax.

0

6 Answers 6

209

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;
Sign up to request clarification or add additional context in comments.

9 Comments

Excellent answer -- note also emplace in C++11 as a terse alternative to insert.
Why is that std::<map>::value_type there in the insert call?
Why does the default constructor need to be user defined?
@schuess I see no reason why it does: = default should work just fine.
The condition 'Map does not already contain such an object' would be evaluated at runtime. Why a compile time error?
|
7

Yes. Values in STL containers need to maintain copy semantics. IOW, they need to behave like primitive types (e.g. int) which means, among other things, they should be default-constructible.

Without this (and others requirements) it would be needlessly hard to implement the various internal copy/move/swap/compare operations on the data structures with which STL containers are implemented.

Upon reference to the C++ Standard, I see my answer was not accurate. Default-construction is, in fact, not a requirement:

From 20.1.4.1:

The default constructor is not required. Certain container class member function signatures specify the default constructor as a default argument. T() must be a well-defined expression ...

So, strictly speaking, your value type only needs to be default constructible if you happen to be using a function of the container that uses the default constructor in its signature.

The real requirements (23.1.3) from all values stored in STL containers are CopyConstructible and Assignable.

There are also other specific requirements for particular containers as well, such as being Comparable (e.g. for keys in a map).


Incidentally, the following compiles with no error on comeau:

#include <map>

class MyClass
{
public:
    MyClass(int t);
};

int main()
{
    std::map<int, MyClass> myMap;
}

So this might be a g++ problem.

2 Comments

Do you think bb could be on to something regarding [] operator?
That code probably compiles because you aren't calling myMap[]
5

Check requirements of stored type of the stl::map. Many stl collection require that stored type contains some specific properties (default constructor, copy constructor, etc.).

Constructor without arguments is needed by the stl::map, because it's used, when operator[] is invoked with the key, which hasn't already been kept by the map. In this case the operator[] inserts the new entry consisting of the new key and value constructed using parameterless constructor. And this new value is then returned.

Comments

0

assume you have the following

class Person
{
public:
    Person(int age) :age(age){}
    Person() {} // default ctor
    int age;
};

map<int, Person> m;

// accessing not-existent key, results in assigning default value to that key
m[10];

// creates default object for key:20 first then assigns age
m[20].age = 32;

what should happen if you wanna assign age for a nonexistent key?

for languages with null type such as javascript the map returns null and its is up to user to check for it before accessing the object or its internal fields.

c++ went a different approach and creates the Person using default constructor so the null is avoided all together

Comments

-2

Check if:

  • You forgot the ';' after class declaration.
  • MyType should've been declared accordingly.
  • No default constructor there...

The std::map declaration seems correct, I think.

1 Comment

Compiles fine if I add a default constructor.
-2

Most likely because std::pair requires it. std::pair holds two values using value semantics so you need to be able to instantiate them without parameters. So the code uses std::pair in various places to return the map values to the caller and this is commonly done by instantiating an empty pair and assigning the values into it before returning the local pair.

You could get around this with smart pointers using a map<int, smartptr<MyClass> > but that adds the overhead of checking for null pointers.

1 Comment

+0. pair<T, U> can be used just fine with types T and U lacking default constructors -- the only thing that cannot be used in this case is pair<T, U>'s own default constructor. No decent-quality implementation of map<K, V> would use this default constructor because it limits what K and V can be.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.