Использование класса в качестве параметра шаблона в собственной деклерации

(Я знаю, что это не обычная реализация, но я хотел попробовать эту идею.)

struct TrieNode {
    std::unordered_map<char, TrieNode> next;
};

Этот класс очень хорошо скомпилирован и работает, как и ожидалось, в Visual Studio 2017. Однако он не компилируется с помощью gcc (c++14) (как я и ожидал).

In file included from /usr/include/c++/8/bits/stl_algobase.h:64,
                 from /usr/include/c++/8/bits/char_traits.h:39,
                 from /usr/include/c++/8/ios:40,
                 from /usr/include/c++/8/ostream:38,
                 from /usr/include/c++/8/iostream:39,
                 from prog.cpp:1:
    /usr/include/c++/8/bits/stl_pair.h: In instantiation of ‘struct std::pair<const char, TrieNode>’:
    /usr/include/c++/8/bits/stl_vector.h:1610:27:   required from ‘struct __gnu_cxx::__aligned_buffer<std::pair<const char, TrieNode> >’
    /usr/include/c++/8/bits/hashtable_policy.h:234:43:   required from ‘struct std::__detail::_Hash_node_value_base<std::pair<const char, TrieNode> >’
    /usr/include/c++/8/bits/hashtable_policy.h:280:12:   required from ‘struct std::__detail::_Hash_node<std::pair<const char, TrieNode>, false>’
    /usr/include/c++/8/bits/hashtable_policy.h:2027:49:   required from ‘struct std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<const char, TrieNode>, false> > >’
    /usr/include/c++/8/bits/hashtable.h:173:11:   required from ‘class std::_Hashtable<char, std::pair<const char, TrieNode>, std::allocator<std::pair<const char, TrieNode> >, std::__detail::_Select1st, std::equal_to<char>, std::hash<char>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >’
    /usr/include/c++/8/bits/unordered_map.h:105:18:   required from ‘class std::unordered_map<char, TrieNode>’
    prog.cpp:9:37:   required from here
    /usr/include/c++/8/bits/stl_pair.h:215:11: error: ‘std::pair<_T1, _T2>::second’ has incomplete type
           _T2 second;                /// @c second is a copy of the second object
               ^~~~~~
    prog.cpp:7:8: note: forward declaration of ‘struct TrieNode’
     struct TrieNode {
            ^~~~~~~~ 

Интересно, как реализация Visual C++ не имела с этим проблем? Что говорят стандарты для таких случаев?


person E_g    schedule 12.09.2019    source источник
comment
Связано с class-or-struct-self-reference-by-template   -  person Jarod42    schedule 12.09.2019


Ответы (1)


Я не могу сказать вам, почему это работало в MSVS2017, но по стандарту использование неполного типа с std::unordered_map является неопределенным поведением. Контекст полного класса класса определяется [ class.mem]/6 как

Полный контекст класса — это

  • тело функции ([dcl.fct.def.general]),

  • аргумент по умолчанию,

  • noexcept-specifier ([except.spec]) или

  • инициализатор члена по умолчанию

Только в тех местах имя класса обозначает полный тип. Так как мы не находимся ни в одном из этих мест, название именует неполный тип. Если мы проверяем, можем ли мы использовать это с std::unordered_map, мы проверяем [ res.on.functions]/2 и имеем

В частности, последствия не определены в следующих случаях: [...]

  • Если неполный тип ([basic.types]) используется в качестве аргумента шаблона при создании экземпляра компонента шаблона или оценке концепции, если это специально не разрешено для этого компонента.

Так вообще это не разрешено. Нам нужно проверить [container.requirements] и [unord.map], но там ничего не говорится о том, что неполный тип разрешен. Это означает, что мы возвращаемся к общему правилу, и это не разрешено.

person NathanOliver    schedule 12.09.2019