问题
This is a followup question to assigning-of-unordered-map-to-pair-of-objects. This is a question about the interpretation of the compiler errors (and not a repeat question, as that question was already fully answered). I was asked whether I took a look at the errors, and to post the errors so that others might benefit from an understanding. This is the first error for this:
#include <bits/stdc++.h>
using namespace std;
struct foo {
int n;
foo(int n): n(n) {};
// foo(): n(0) {};
};
int main(){
unordered_map<int, pair<foo,foo>> m;
m[3] = make_pair(foo(1),foo(2));
}
And here is the first error after compilation (rest omitted for now):
g++ -std=c++17 -Weffc++ -Wall -Wextra -Wsign-conversion pairs.cpp -o ../build/pairs.bin
In file included from /usr/include/c++/8/functional:54,
from /usr/include/x86_64-linux-gnu/c++/8/bits/stdc++.h:71,
from pairs.cpp:1:
/usr/include/c++/8/tuple: In instantiation of ‘std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {int&&}; long unsigned int ..._Indexes1 = {0}; _Args2 = {}; long unsigned int ..._Indexes2 = {}; _T1 = const int; _T2 = std::pair<foo, foo>]’:
/usr/include/c++/8/tuple:1657:63: required from ‘std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {int&&}; _Args2 = {}; _T1 = const int; _T2 = std::pair<foo, foo>]’
/usr/include/c++/8/ext/new_allocator.h:136:4: required from ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const int, std::pair<foo, foo> >; _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Tp = std::__detail::_Hash_node<std::pair<const int, std::pair<foo, foo> >, false>]’
/usr/include/c++/8/bits/alloc_traits.h:475:4: required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const int, std::pair<foo, foo> >; _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Tp = std::__detail::_Hash_node<std::pair<const int, std::pair<foo, foo> >, false>; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::__detail::_Hash_node<std::pair<const int, std::pair<foo, foo> >, false> >]’
/usr/include/c++/8/bits/hashtable_policy.h:2093:36: required from ‘std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type* std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_allocate_node(_Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const int, std::pair<foo, foo> >, false> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const int, std::pair<foo, foo> >, false>]’
/usr/include/c++/8/bits/hashtable_policy.h:736:8: required from ‘std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type& std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::operator[](std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type&&) [with _Key = int; _Pair = std::pair<const int, std::pair<foo, foo> >; _Alloc = std::allocator<std::pair<const int, std::pair<foo, foo> > >; _Equal = std::equal_to<int>; _H1 = std::hash<int>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type = std::pair<foo, foo>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = int]’
/usr/include/c++/8/bits/unordered_map.h:978:20: required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type&&) [with _Key = int; _Tp = std::pair<foo, foo>; _Hash = std::hash<int>; _Pred = std::equal_to<int>; _Alloc = std::allocator<std::pair<const int, std::pair<foo, foo> > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = std::pair<foo, foo>; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = int]’
pairs.cpp:11:6: required from here
/usr/include/c++/8/tuple:1668:70: error: no matching function for call to ‘std::pair<foo, foo>::pair()’
second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
回答1:
The library code is trying to default construct a pair
of foo
(that's what std::pair<foo, foo>::pair()
in the last line means). But it can't do that because foo
doesn't have a default constructor.
All the library templates make certain requirements on the types used to parameterise them. It seems std::unordered_map
requires a default constructor.
I have a feeling (but I'm not going to look it up) that you could avoid this problem if you didn't use operator[]
to insert into your map (in other words it's operator[]
that needs the default constructor). If this is a problem try using emplace
instead.
m.emplace(3, make_pair(foo(1),foo(2)));
回答2:
std::unordered_map::operator[]
needs to default construct new element. If you class is not default constructible, you can't use operator[]
to create new element and then assign to it.
Use emplace
or insert
or insert_or_assign
to insert new element into a map.
回答3:
In general try to read these error message backwards.
1. Look at the last line (broken up to fit better on the available space)
/usr/include/c++/8/tuple:1668:70: error: no matching function for call
to ‘std::pair<foo, foo>::pair()’
second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
This tells you immediately what's going on: There is a call somewhere (we'll have to find out where it originates), to a function that doesn't exist. That function is called
std::pair<foo, foo>::pair()
This is a constructor of a type from the standard library. Specifically it is a constructor without arguments.
2. So let's have a look at the documentation for pair. Oddly enough, there seems to be a constructor without arguments there.
3. Why doesn't it exist? Let's read on in the documentation:
1) Default constructor. Value-initializes both elements of the pair, first and second.
This constructor participates in overload resolution if and only ifstd::is_default_constructible_v<first_type>
andstd::is_default_constructible_v<second_type>
are both true. This constructor is explicit if and only if either first_type or second_type is not implicitly default-constructible.
(emphasis mine)
4. Great let's check the condition, first_type
and second_type
are both the type foo
in this case. Let's examine this type:
struct foo {
int n;
foo(int n): n(n) {};
// foo(): n(0) {};
};
There is a foo(int)
constructor for this type, and a commented out foo()
constructor. There should be an implicit default constructor, right? No:
If no user-declared constructors of any kind are provided for a class type (struct, class, or union), the compiler will always declare a default constructor as an inline public member of its class.
We have a foo(int)
, hence the compiler does not generate foo()
on its own. Aha, the call fails because indeed the function does not exist.
5. Okay, but why is it called in the first place? I don't remember calling such a function. -> Look at the "next" line in your error:
pairs.cpp:11:6: required from here
That line (line 11 in pairs.cpp) reads:
m[3] = make_pair(foo(1),foo(2));
6. What does the documentation have to say about the first function called there? std::unordered_map<int,std::pair<foo,foo>>::operator[]:
Returns a reference to the value that is mapped to a key equivalent to key, performing an insertion if such key does not already exist. ... When the default allocator is used, this results in the key being copy constructed from key and the mapped value being value-initialized.
7. What does value-initialisation mean?
T() (1)
new T () (2)
Class::Class(...) : member() { ... } (3)
T object {}; (4) (since C++11)
T{} (5) (since C++11)
new T {} (6) (since C++11)
Class::Class(...) : member{} { ... } (7) (since C++11)
Remember, T
here is the value_type
of your unordered_map
, which in turn is pair<foo,foo>
. That looks suspiciously like the call the compiler couldn't find earlier.
8. If we want to use std::unordered_map<K,T>::operator[]
, T
better be value initialisable, e.g. by providing a T::T()
constructor. That's where we have to fix it.
Conclusion
Read the error meticulously, check out the documentation for stuff you don't already know and follow up on the requirements of the functions you are using. And remember to read the error message backwards to figure out where the error was encountered, and in which of your own files it was triggered. Then examine that line.
来源:https://stackoverflow.com/questions/60776989/parsing-compilation-error-no-matching-function-for-call-to-stdpair-pair