C++ []运算符访问无序的_映射时出错

C++ []运算符访问无序的_映射时出错,c++,c++11,g++,unordered-map,C++,C++11,G++,Unordered Map,我在一张无序的地图上偶然发现了一个奇怪的问题 首先,我生成了一个无序的映射,并在表中插入了一条记录(“Bob”,Person(1,“Bob”))。然后,我尝试使用带有键“Bob”的[]运算符访问该记录,但出现了一个错误 代码如下: #include<iostream> #include<unordered_map> using namespace std; class Person { public: int play; stri

我在一张无序的地图上偶然发现了一个奇怪的问题

首先,我生成了一个无序的映射,并在表中插入了一条记录(“Bob”,Person(1,“Bob”))。然后,我尝试使用带有键“Bob”的[]运算符访问该记录,但出现了一个错误

代码如下:

#include<iostream>
#include<unordered_map>
using namespace std;

class Person
{
    public:
        int play;
        string name;
        Person(int p, string n):play(p), name(n) {}
};

int main()
{
    unordered_map<string,Person> test;
    test.insert(std::make_pair("haha",Person(1,"haha")));
    cout<<test["haha"].name<<endl;
    return 0;
}
#包括
#包括
使用名称空间std;
班主任
{
公众:
智力游戏;
字符串名;
Person(int p,string n):play(p),name(n){}
};
int main()
{
无序图试验;
测试插入(标准:配对(“哈哈”,人(1,“哈哈”));

cout问题不在于[]运算符。实际问题在于编译器执行以下行时:

cout<<test["haha"].name<<endl;

cout无序映射的映射类型必须是
operator[]
下的DefaultConstructible。即,如果您希望能够使用
operator[]
,Person()必须具有默认构造函数

或者,使用at():

intmain()
{ 
标准:无序图试验;
测试插入(标准:配对(“哈哈”,人(1,“哈哈”));

std::cout正如注释和另一个答案中所解释的,可能需要创建一个对象:如果没有为键找到任何对象,它将插入一个默认构造的元素并返回该元素

如果这不是您想要的,您可以简单地尝试查找元素,并仅在存在时使用它:

auto it = map.find("haha");
if(it != map.end()) {
    cout << it->second.name;
}
唯一的不便是您必须通过
person::second.play
访问数据,如
play
。解决此问题的一种方法是定义

struct person : private personMap::value_type
{
     using base = personMap::value_type;
     // must not add new data members to avoid slicing
     personData& data() { return base.second; }
     personData const& data() const { return base.second; }
     int play() const { return data().play; }
     string const&name() const { return base.first; }
};

auto fred = static_cast<person&>(map["fred"]);
struct person:private personMap::value\u type
{
使用base=personMap::value\u类型;
//不得添加新数据成员以避免切片
personData&data(){return base.second;}
personData常量&data()常量{return base.second;}
int play()常量{return data().play;}
字符串常量&name()常量{return base.first;}
};
自动弗雷德=静态施法(映射[“弗雷德]);

给出的答案是正确的。我只想补充一下为什么在这种情况下编译器不提供默认构造函数:

从网上

如果为类类型(struct、class或union)提供了任何类型的用户声明构造函数,则编译器将始终将默认构造函数声明为其类的内联公共成员

但是,如果提供了任何类型的用户声明构造函数,编译器将而不是生成默认构造函数

如果存在一些用户声明的构造函数,用户仍然可以强制编译器自动生成默认构造函数,而默认构造函数将以关键字default隐式声明

因此,您可以在类的公共部分的代码中添加以下行,您的代码将按预期工作:

Person() = default;

您需要包含字符串头,并且需要Person的默认构造函数。
test[“haha”]。name
可能需要构造一个对象,如果没有默认构造函数,您就会遇到问题。您可以在错误中看到:
没有匹配的函数来调用“Person::Person()
完成。感谢您的帮助。我尚未考虑[]的可能需求运算符。这确实是个问题。我不得不对这篇文章投反对票,因为编译器清楚地说明了问题所在,但你还是这样问。谢谢。我错误地忽略了这条规则,认为我的代码不会构造新的Person对象,这是根本原因。非常有用且有见地。非常感谢!是的,我注意到了这条规则如果找不到该键的任何元素,它将插入一个默认构造的元素并返回该元素“无序映射。但我认为我的程序可以找到密钥,因此我忽略了编译器的必要检查,这是根本原因。谢谢。这是一个重要的教训。是的,我忽略了规则,并收到了编译器的投诉。谢谢。@ronald您可以通过使用insert、emplace、find和at来避免实现默认构造函数。”这是非常重要的一课。
auto it = map.find("haha");
if(it != map.end()) {
    cout << it->second.name;
}
struct personData
{
    int play; // etc
};

using personMap = unordered_map<string,personData>;
using person = personMap::value_type;
struct person : private personMap::value_type
{
     using base = personMap::value_type;
     // must not add new data members to avoid slicing
     personData& data() { return base.second; }
     personData const& data() const { return base.second; }
     int play() const { return data().play; }
     string const&name() const { return base.first; }
};

auto fred = static_cast<person&>(map["fred"]);
Person() = default;