为什么我需要声明默认构造函数以便在返回 unordered_map 值时进行编译?

Why do I need to declare a default constructor in order to compile when returning an unordered_map value?

除非我取消注释默认构造函数声明,否则此示例无法编译:

#include<unordered_map>
#include <iostream>

struct foo{
    int data;
    /*foo(){
        data = 0;
        std::cout << "DEFAULT\n";
    }*/
    foo(int d){
        data = d;
        std::cout << "PARAM\n";
    }
};

struct bar{
    std::unordered_map<int, foo> map;
    foo getElem(int i){
        return map[i];
    }
};

int main() {
    bar b;
    foo f1(1);
    foo f2(2);
    b.map.insert({1,f1});
    b.map.insert({2,f2});

    foo f3 = b.getElem(1);
}
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/tuple:1689:70: error: no matching function for call to 'foo::foo()'
 1689 |         second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
      |                                                                      ^

当我取消注释默认构造函数声明并成功编译时,面包屑显示从未调用过默认构造函数。

这是怎么回事?类似的问题是由于 most vexing parse,但没有任何显式构造函数调用,我不确定这里是否是这种情况。

提供了关于为什么 unordered_map 会隐式调用默认构造函数的提示。问题是描述的记录行为和 MVP 的某种组合吗?

link to godbolt

当编译器编译这一行时:

foo f3 = b.getElem(1);

1 作为参数传递给函数(除非发生大量优化魔术,但不能保证)。此函数无法知道 1 永远不会是丢失的键。因此,调用映射中分配条目的代码。如果密钥丢失,传递给此代码的是什么?是的,一个默认构造的 foo.

所以,简而言之,你知道

the default constructor is not ever called

但编译器和链接器没有。