How to have an unordered_map where the value type is the class it's in?
The C++ standard specifies for the various smart pointers that the template parameter is allowed to be an incomplete type.
The 2017 and later versions of the standard allow a container's value_type
to be an incomplete type when instantiating the class template only for the container templates std::forward_list
, std::list
, and std::vector
, and only if the allocator type satisfies the "allocator completeness requirements". The default allocator template std::allocator
always satisfies the allocator completeness requirements. Instantiating any member of the container class template still requires the value_type
to be complete.
For any other standard container type, this information is not given. In cases where it is unspecified, an implementation is allowed to accept an incomplete type for one container class template and not another, and still be conformant.
To make your code portable, avoid making containers of any type before the type is completed, except in the cases specifically permitted by the standard.
Formally, the general constraint is found in the following rule ([res.on.functions]) which applies to your code:
In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ standard library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.
In particular, the effects are undefined in the following cases:
...
- if an incomplete type is used as a template argument when instantiating a template component, unless specifically allowed for that component.
The three statements specifically allowing an incomplete template argument for forward_list
, list
, and vector
are found in sections [forwardlist.overview], [list.overview], and [vector.overview].
Custom std::set as value type to unordered_map
Lambdas are never default constructible before C++20 (see ClosureType::ClosureType()
section on cppreference), so you must pass the SetCompare
variable when constructing a MySet
. Using -std=c++20
/-std=c++2a
on clang or GCC should work (demo), as well as /std:c++latest
on MSVC.
To work around this limitation in earlier C++ versions define SetCompare
as a functor type:
struct SetCompare
{
bool operator()(const string& a, const string& b)
{
size_t L = a.length();
size_t R = b.length();
if (L == R) return (a < b);
return L < R;
}
};
and use this type when defining MySet
using MySet = std::set<std::string, SetCompare>;
C++ accessing value type of an unordered_map
class std::any
is only since C++17.
If you want to compile it with C++14 you can use boost::any
calss from boost lib
Setting unordered_map as value for unordered_map
You don't really need to store the inner map as a pointer. Just let the outer map own the inner one like this:
std::unordered_map<std::string,
std::unordered_map<int, std::unique_ptr<Foo>>> data;
data[postCode][int(k1.Kundennr)] = new Foo(&k1);
I advise against using raw pointers here, since there is a high risk of leaking memory if you replace a value in the inner map. Using std::unique_ptr
makes it so much simpler to deal with pointers.
Your exception probably happens because unordered_map::at
requires the value to contain the key you are asking for, in order to retrieve the reference to the value it is mapping. If you use operator[]
instead like in my example, the map will create an entry if the key is not found.
If you still insist on having the outer map's values to be pointers to the inner map, then you can add elements like this:
std::unordered_map<int, Foo*> &inner = *data[postCode];
inner[int(k1.Kundennr)] = new Foo(&k1);
or
data[postCode]->operator[](int(k1.Kundennr)) = new Foo(&k1);
unordered_map value type with private constructor
Your error will be referring to this line:
return &myclasses[str];
That's because operator[]
has to default construct a value if it's not there in the map for the particular key. Since that operator isn't a friend of myclass
, you get the access error.
Instead, you should take advantage of the fact that emplace
returns the iterator that it inserted at:
myclass *make_myclass(const std::string &str) {
auto it = myclasses.find(str);
if (it == myclasses.end()) {
it = myclasses.emplace(str, myclass{}).first;
}
return &it->second;
}
This also has the added benefit of not having to do a second lookup on str
.
std::unordered_map with custom value type, operator[]
You are getting this error because of lack of default constructor in your CSVRecord.
How does operator[]
work?
operator[]
searches for the key provided to it and if element is already there in map it returns the reference to that element. If element is not there then it adds the key with default constructed object. In your case it was not able to find appropriate constructor hence emitted error.
c++ unordered_map for user defined data type
This is the expected behavior. According to the documentation
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.
Internally, std::unordered_map
's operator[]
needs to create a new instance of the data class when the key is not found, and return you a reference to it. All this happens before the assignment, while the left-hand side of the expression is being evaluated. That's what causing the error about the missing default constructor.
Hence, if you wish to use map's operator[]
with your value type, you need to provide a way to default-construct your value object.
Note 1: Copying of the value does take place. However, it starts only after the operator[]
returns.
Note 2: There is no point in defining a separate pass-by-value constructor for your struct node
, because the other constructor (i.e. node(const string& a)
) is perfectly capable of handling both cases.
Note 3: There is no point in allocating the node dynamically. Currently, it leaks memory. An assignment vertices[name] = node(name);
would do the same thing without a leak.
Related Topics
Why Use Functors Over Functions
How to Read a Binary File into a Vector of Unsigned Chars
In C++, Is It Safe/Portable to Use Static Member Function Pointer for C API Callbacks
C++ Access Static Members Using Null Pointer
How Do Sizeof(Arr)/Sizeof(Arr[0]) Work
How to Use C++ Classes with Ctypes
When to Use the Brace-Enclosed Initializer
Simple Illumination Correction in Images Opencv C++
Regex Replace with Callback in C++11
C++ Program Converts Fahrenheit to Celsius
Lifetime of a Std::Initializer_List Return Value
Can a Recursive Function Be Inline
Start Windows Service from Application Without Admin Right(C++)
How to Render an Opengl Frame in C++ Builder
Why Unsigned Int 0Xffffffff Is Equal to Int -1
Should I Unify Two Similar Kernels with an 'If' Statement, Risking Performance Loss
How to Know If One Shared Library Depends on Another Shared Library or Not