Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have the following minimal example reproducing an error in my code:

#include <unordered_map>
#include <iostream>

class B 
{
public:
    B(int b) : m_b{ b } {}
    int m_b;
};

int main()
{    
    using std::cout, std::endl;
    std::unordered_map<int, B> ab{};

    ab[1] = B(3);
    //ab.insert(std::pair<int, B>(1, B(3)));

    cout << ab[1].m_b << endl;
}

This fails with a long and unwieldy error which basically amounts to saying that there is no constructor for B without any arguments. The error stems from ab[1] = B(3) Why is that needed? And why does using insert instead of operator[] not need that constructor?

Bonus points for why this line in my original code:

Vec2 pos{ m_orbits[&p].positionAtTime(m_time + dt) };

also requires a non - parameterized constructor. I could not reproduce that error in my minimal example, but m_orbits is an unordered map with pointers to Particle objects as keys and Orbit objects as values. positionAtTime is a const member function of Orbit that calculates the position of a particle in the orbit at a certain time.

like image 920
David Hambraeus Avatar asked Jan 21 '26 17:01

David Hambraeus


1 Answers

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.

like image 96
JeJo Avatar answered Jan 23 '26 07:01

JeJo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!