msvc++和clang如何知道这个结构的大小,甚至不需要前向声明?

编码

这是这个看似无辜的几行代码。

#include <iostream>
#include <string>
#include <unordered_map>

struct Tag
{
    using name_t        = std::string;
    using tag_map_t     = std::unordered_map<name_t, Tag>;

    name_t              name;
    tag_map_t           children = {};
};

int main(void)
{
    auto foo = Tag{"foo"};
    auto bar = Tag{"bar"};
    foo.children["bar"] = bar;
    std::cout << foo.name << " -> " << foo.children["bar"].name << std::endl;

    return 0;
}

让我们尝试编译它

? clang++ --version
clang version 11.0.1
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:LLVMbin

? clang++ -std=c++14 -Weverything -Werror -Wno-c++98-compat tag.cpp -o tag.exe && tag.exe
foo -> bar

msvc++

? cl
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27031.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

? vcvars64.bat && cl.exe /std:c++14 /W4 /WX /EHsc tag.cpp /Fe:tag.exe && tag.exe
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.9.13
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27031.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

tag.cpp
Microsoft (R) Incremental Linker Version 14.16.27031.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:tag.exe
tag.obj
foo -> bar

海湾合作委员会

? gcc --version
gcc (GCC) 10.2.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

? g++ -std=c++14 -pedantic -Wall tag.cpp -o tag.exe && tag.exe
In file included from /usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/bits/stl_algobase.h:64,
                 from /usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/bits/char_traits.h:39,
                 from /usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/ios:40,
                 from /usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/ostream:38,
                 from /usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/iostream:39,
                 from tag.cpp:1:
/usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/bits/stl_pair.h: In instantiation of ‘struct std::pair<const std::basic_string<char>, Tag>’:
/usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/ext/aligned_buffer.h:91:28:   required from ‘struct __gnu_cxx::__aligned_buffer<std::pair<const std::basic_string<char>, Tag> >’
/usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/bits/hashtable_policy.h:233:43:   required from ‘struct std::__detail::_Hash_node_value_base<std::pair<const std::basic_string<char>, Tag> >’
/usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/bits/hashtable_policy.h:264:12:   required from ‘struct std::__detail::_Hash_node<std::pair<const std::basic_string<char>, Tag>, true>’
/usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/bits/hashtable_policy.h:1973:13:   required from ‘struct std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<const std::basic_string<char>, Tag>, true> > >’/usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/bits/hashtable.h:173:11:   required from ‘class std::_Hashtable<std::basic_string<char>, std::pair<const std::basic_string<char>, Tag>, std::allocator<std::pair<const std::basic_string<char>, Tag> >, std::__detail::_Select1st, std::equal_to<std::basic_string<char> >, std::hash<std::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/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/bits/unordered_map.h:105:18:   required from ‘class std::unordered_map<std::basic_string<char>, Tag>’
tag.cpp:11:37:   required from here
/usr/lib/gcc/x86_64-pc-msys/10.2.0/include/c++/bits/stl_pair.h:218:11: error: ‘std::pair<_T1, _T2>::second’ has incomplete type
  218 |       _T2 second;                ///< The second member
      |           ^~~~~~
tag.cpp:5:8: note: forward declaration of ‘struct Tag’
    5 | struct Tag
      |        ^~~

我很确定gcc是对的,但我不明白如何clang以及visual studio如何编译它并将其转换为甚至可以运行的二进制文件。

他们怎么知道的大小TagTag是递归数据结构,对吗?所以,在某种程度上,这些编译器可以计算出的大小Tag,即使我没有存储像shared_ptrunordered_map,但Tag本身。这是有效的 C++ 代码吗?

为什么我什至不需要为struct? 我认为在 C++ 中,编译器至少需要“看到”一个名称的声明才能将其作为有效名称,所以我很惊讶这里似乎并非如此。至少根据clangvisual studio。我在这里错过了什么重要的细节?

修复这段代码以使其工作gcc非常简单,所以我的问题实际上是关于它如何与其他两个编译器一起工作。

回答

模板类型可以用不完整的参数来完成。

struct incomplete;
template<class T>struct tag_t{using type=T;};

thentag_t<incomplete>是一个完整的类型。它sizeof1并且它是一个空类型。

这不是模板所特有的;incomplete*也完成了,原样incomplete(*)()

几乎所有 std 容器的大小都不依赖于它们的模板参数的大小;数组是明显的例外。

在实践中,可以使用不完整类型编写高质量容器,直到您尝试执行某些需要类型完整的操作,例如将数据放入其中。

标准允许的不完整性随着后来的标准修订变得更加普遍。可能您的 gcc 标准库不太现代。


以上是msvc++和clang如何知道这个结构的大小,甚至不需要前向声明?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>