Почему следующий код компилируется с использованием clang, но не gcc - PullRequest
3 голосов
/ 22 апреля 2020
#include <iostream>
#include <unordered_map>
#include <string>

struct tree_node {
  // tree_node() : attrib_val{"null"} {}
  std::unordered_map<std::string, tree_node> child;
};
int main(int argc, char const *argv[])
{
  return 0;
}

Этот код прекрасно компилируется на моей ма c с помощью clang:

$ g++ --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

$ g++ -std=c++11 test.cpp
$ 

На моей linux машине с g cc 9.1.0 я получаю следующую ошибку :

In file included from /usr/um/gcc-9.1.0/include/c++/9.1.0/bits/stl_algobase.h:64,
                 from /usr/um/gcc-9.1.0/include/c++/9.1.0/bits/char_traits.h:39,
                 from /usr/um/gcc-9.1.0/include/c++/9.1.0/ios:40,
                 from /usr/um/gcc-9.1.0/include/c++/9.1.0/ostream:38,
                 from /usr/um/gcc-9.1.0/include/c++/9.1.0/iostream:39,
                 from test.cpp:1:
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/stl_pair.h: In instantiation of ‘struct std::pair<const std::__cxx11::basic_string<char>, tree_node>’:
/usr/um/gcc-9.1.0/include/c++/9.1.0/ext/aligned_buffer.h:91:28:   required from ‘struct __gnu_cxx::__aligned_buffer<std::pair<const std::__cxx11::basic_string<char>, tree_node> >’
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/hashtable_policy.h:233:43:   required from ‘struct std::__detail::_Hash_node_value_base<std::pair<const std::__cxx11::basic_string<char>, tree_node> >’
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/hashtable_policy.h:264:12:   required from ‘struct std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, tree_node>, true>’
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/hashtable_policy.h:2016:13:   required from ‘struct std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, tree_node>, true> > >’
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/hashtable.h:173:11:   required from ‘class std::_Hashtable<std::__cxx11::basic_string<char>, std::pair<const std::__cxx11::basic_string<char>, tree_node>, std::allocator<std::pair<const std::__cxx11::basic_string<char>, tree_node> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char> >, std::hash<std::__cxx11::basic_string<char> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >’
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/unordered_map.h:105:18:   required from ‘class std::unordered_map<std::__cxx11::basic_string<char>, tree_node>’
test.cpp:7:46:   required from here
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/stl_pair.h:215:11: error: ‘std::pair<_T1, _T2>::second’ has incomplete type
  215 |       _T2 second;                /// @c second is a copy of the second object
      |           ^~~~~~
test.cpp:5:8: note: forward declaration of ‘struct tree_node’
    5 | struct tree_node {

Не нравится tree_node как значение в unordered_map по некоторым причинам.

Ответы [ 2 ]

5 голосов
/ 22 апреля 2020

Это неопределенное поведение, [res.on.functions]/2.5:

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

Это досадный случай, когда мне в основном приходится доказывать отрицательность этого ответа, чтобы он был действительным, но я не нахожу места в стандарте, где упоминается исключение, позволяющее использовать неполный тип в std::map. Поэтому эта программа может делать все что угодно. В частности, хотя Clang компилирует его сейчас, он может перестать работать в любой момент в будущем, и есть также шанс, что скомпилированный код map специализация не будет работать правильно . В некоторых контейнерах , особенно std::vector, есть пункт, позволяющий создавать их экземпляры в неполных типах при соответствующих условиях. Но это случай неопределенного поведения, и поэтому компиляторы не обязаны предупреждать вас или сообщать об ошибке. Измените вашу программу, чтобы избежать этого. Я полагаю, что следующее было бы законным, не заставляя вас хранить слишком много дополнительных указателей.

struct tree_node {
  std::unique_ptr<std::unordered_map<std::string, tree_node>> child;
};

std::unique_ptr является исключением из общего правила no- go - это нормально для создания экземпляра в неполный тип (но некоторые его члены не такие слабые). Я считаю, что это означает, что std::unordered_map<std::string, tree_node> не обязательно должно быть завершено в точке в определении tree_node, где требуется завершение специализации std::unique_ptr, и поэтому специализация std::unordered_map не вызывается и UB избегается, поскольку tree_node не требуется для завершения. Обратите внимание, что вы все еще можете писать конструкторы, функции, деструктор и т. Д. c. без беспокойства, поскольку все эти определения неявно перемещаются из определения класса и после него, и tree_node становится полным после окончания определения класса.

0 голосов
/ 22 апреля 2020

Изменение:

struct tree_node {
  // tree_node() : attrib_val{"null"} {}
  std::unordered_map<std::string, tree_node> child;
};

Кому:

struct tree_node {
  // tree_node() : attrib_val{"null"} {}
  std::unordered_map<std::string, tree_node *> child;
};

Устраняет проблему компиляции go. Имеет ли это какое-либо отражение в том, что вы хотели?

...