How to Have an Unordered_Map Where the Value Type Is the Class It's In

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



Leave a reply



Submit