Search code examples
c++unordered-mapdefault-constructormost-vexing-parse

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


This example fails to compile unless I uncomment the default constructor declaration:

#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))...)
      |                                                                      ^

When I uncomment the default constructor declaration and successfully compile, the breadcrumbs show that the default constructor is not ever called.

What is going on here? A similar problem is due to a most vexing parse, but without any explicit constructor calls I am not sure if that is the case here.

This answer provides hints as to why an unordered_map would implicitly call a default constructor. Is the issue some combination of the documented behavior described and MVP?

link to godbolt


Solution

  • When the compiler compiles this line:

    foo f3 = b.getElem(1);
    

    1 is passed as a parameter to a function (unless a whole lot of optimization magic occured, which is not guaranteed). This function cannot know that 1 will never be a missing key. As such, the code that allocates an entry in the map is called. And what is passed to this code, if the key is missing ? Yep, a default-constructed foo.

    So, in a nutshell, you know that

    the default constructor is not ever called

    but the compiler and linker don't.