为什么 std::map::operator[] 赋值需要一个无参数的构造函数?

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

我有以下最小示例在我的代码中重现错误:

#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;
}

这失败了,并出现了一个冗长而笨拙的错误,这基本上相当于说 B 没有不带任何参数的构造函数。错误源于 ab[1] = B(3) 为什么需要这样做?为什么使用 insert 而不是 operator[] 不需要那个构造函数?

加分为什么在我的原始代码中有这一行:

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

还需要一个非参数化的构造函数。我无法在我的最小示例中重现该错误,但是 m_orbits 是一个无序映射,其中指向 Particle 对象的指针作为键,指向 Orbit 对象的指针作为值。 positionAtTimeOrbit的一个const成员函数,计算某时刻粒子在轨道上的位置。

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

这是因为 std::map::operator[] 要求 mapped_type(即在您的情况下 B)是默认可构造的。

  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.

当您提供用户定义的构造函数(即 B(int b))时,compiler will not generate a default constructor automatically,因此 A 不能默认可解释。

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.

因此,出现上述错误!


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

因为 std::map::insert 依赖于 value_type(即 std::pair<const Key, T>)。对于您的 ab,这是 std::pair<const int, B>。从 cppreference.com 函数重载:

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.

所以只要 B 可以构建,std::pair<const int, B> 也可以构建,std::map::insert 可以工作。