Why does the following code compile using clang but not gcc

点点圈 提交于 2021-02-16 18:24:25

问题


#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;
}

This code compiles just fine on my mac with 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
$ 

On my linux machine, with gcc 9.1.0, I get the following error:

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 {

It doesn't like the tree_node as a value in unordered_map for some reason.


回答1:


This is undefined behavior, by [res.on.functions]/2.5:

[The effects are undefined if] an incomplete type ([basic.types]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.

This is an annoying case where I basically have to prove a negative for this answer to be valid, but I find no place in the standard that mentions an exception that allows you to use an incomplete type in a std::map. Therefore, this program can do anything. In particular, though Clang compiles it now, it may stop working at any point in the future, and there's also a chance that the compiled code map specialization doesn't work properly. Certain containers, especially std::vector, have a clause that allows them to be instantiated at incomplete types under the right conditions. But this case is undefined behavior, and so compilers have no obligation to warn you or error. Change your program somehow to avoid this. I believe the following would be legal, without forcing you to store too many extra pointers.

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

std::unique_ptr is an exception to the general no-go rule—it's OK to instantiate it at an incomplete type (but some of its members aren't as lax). I believe that this means that std::unordered_map<std::string, tree_node> is not required to be complete at the point in the definition of tree_node where the specialization of std::unique_ptr is required to be complete, and so the std::unordered_map specialization is not triggered and UB is avoided since tree_node is not required to be complete. Note that you can still write constructors, functions, a destructor, etc. without worry, since all of those definitions are implicitly moved out of and after the class definition, and tree_node becomes complete after the class definition ends.




回答2:


Changing:

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

To:

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

Makes the compilation problem go away. Whether it has any reflection on what you wanted?



来源:https://stackoverflow.com/questions/61356085/why-does-the-following-code-compile-using-clang-but-not-gcc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!