Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive unordered_map

Tags:

c++

gcc

c++11

clang

I have a tree structure that internally uses unordered map

#include <unordered_map>

struct Node {
        std::unordered_map<int, Node> children;
};

int main() {
        Node a;
}

It works just fine on Apple clang 11.0.3 and MSVC v19.24, but it fails to compile on clang 10.0.0 and gcc 10.1

While regular std::map works just fine on all compilers. I failed to find the reason for this discrepancy. Is there any way to use std::unordered_map as a value for itself? Or pointers is the only solution here?

Here's the compiler explorer link https://godbolt.org/z/6eYch9

Here's an error from gcc:

    #3 with x86-64 gcc 10.1
    In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/unordered_map:43,
                    from <source>:1:
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_pair.h:
In instantiation of 'struct std::pair<const int, Node>':
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/ext/aligned_buffer.h:91:28:
required from 'struct __gnu_cxx::__aligned_buffer<std::pair<const int,
Node> >'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/hashtable_policy.h:233:43:
required from 'struct
std::__detail::_Hash_node_value_base<std::pair<const int, Node> >'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/hashtable_policy.h:279:12:
required from 'struct std::__detail::_Hash_node<std::pair<const int,
Node>, false>'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/hashtable_policy.h:1973:13:
required from 'struct
std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<const
int, Node>, false> > >'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/hashtable.h:173:11:
required from 'class std::_Hashtable<int, std::pair<const int, Node>,
std::allocator<std::pair<const int, Node> >,
std::__detail::_Select1st, std::equal_to<int>, std::hash<int>,
std::__detail::_Mod_range_hashing,
std::__detail::_Default_ranged_hash,
std::__detail::_Prime_rehash_policy,
std::__detail::_Hashtable_traits<false, false, true> >'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/unordered_map.h:105:18:
required from 'class std::unordered_map<int, Node>'

    <source>:4:39:   required from here
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_pair.h:218:11:
error: 'std::pair<_T1, _T2>::second' has incomplete type

      218 |       _T2 second;                ///< The second member
          |           ^~~~~~
    <source>:3:8: note: forward declaration of 'struct Node'
        3 | struct Node {
          |        ^~~~
    Compiler returned: 1
like image 374
Teivaz Avatar asked Jul 12 '20 11:07

Teivaz


People also ask

Can you iterate over unordered_map?

Iterating over a map by using STL Iterator: By creating an iterator of std::map and initializing it to the starting of map and visiting upto the end of map we can successfully iterate over all the elements of map.

What is faster than unordered_map?

But if we consider the worst case (i.e. when the hashing function is not good), the time complexity for all the operations in an unordered_map is O(n). While the worst case time complexity for all the operations in a map is O(log(n)). Hence, in the worst case scenario, a map is faster than an unordered_map.

Is unordered_map better than map?

When it comes to efficiency, there is a huge difference between maps and unordered maps. We must know the internal working of both to decide which one is to be used. You need ordered data. You would have to print/access the data (in sorted order).

Does unordered_map use chaining?

Summary: all practical implementations of std::unordered_set (or unordered_map ) undoubtedly use collision chaining.


2 Answers

STL containers are not required to work with incomplete types. If you don't mind extra indirection, then the workaround is std::map<int, std::unique_ptr<Node>>

like image 142
Alex Guteniev Avatar answered Oct 19 '22 18:10

Alex Guteniev


It's the same problem as doing e.g.

struct Node
{
    Node child;  // An instance of the full structure
};

You can't use a structure (or class) before it's fully defined, which it is at the closing }.

You can however define pointers to the structure, because then the compiler don't need the full structure definition, only know the name of the structure:

struct Node
{
    Node* child;  // Pointer to the structure
};

So to solve your problem, you need a map of pointers:

std::unordered_map<int, Node*> children;
like image 43
Some programmer dude Avatar answered Oct 19 '22 17:10

Some programmer dude