【问题标题】:Compilation error for incomplete vector type (C++17)不完整向量类型的编译错误 (C++17)
【发布时间】:2020-01-05 22:42:15
【问题描述】:

首先是一些背景。正如 C++17 标准所说:

[vector.overview]/3 如果分配器满足分配器完整性要求 17.6.3.5.1,则在实例化向量时可以使用不完整类型 T。 T 应在所产生的向量特化的任何成员被引用之前完成。

我已经尝试了3个场景in this repo(代码复制在底部):

  1. 声明包含不完整向量类型的类(默认 ctor/dtor)并在同一头文件中定义
  2. 使用 a.h 编译包括:clang++ test.cpp --std=c++17
  3. 声明了包含不完整向量类型的类(默认 ctor/dtor)并在头文件和源文件中定义 编译失败
    • 使用 b.h 编译包括:clang++ test.cpp b.cpp --std=c++17
  4. 包含不完整向量类型的类在头文件和源文件中声明和定义(ctor/dtor 显式定义)
  5. 使用 b.h 编译包括:clang++ test.cpp c.cpp --std=c++17

我的问题是,为什么在第二种情况下编译失败,而不是第一种或第三种情况?如果按照标准说引用了 std::vector 的成员,那么为什么在仅标头的情况下没有引用它?这个引用的成员是什么?有什么方法可以在不触及默认关键字或前向声明的情况下编译第二种情况?

附注我尝试过 clang 9.0.0 和 Apple clang 11.0.0 版。

main.cpp

// #include "a.h" // << OK
#include "b.h" // << Compile error
// #include "c.h" // << OK

int main(int argc, char const *argv[])
{
    Bar b;
    return 0;
}

a.h

#include <vector>

struct Foo;

struct Bar
{
    Bar() = default;
    virtual ~Bar() = default;
    std::vector<Foo> foos;
};

struct Foo
{
};

b.h

#include <vector>
struct Foo;
struct Bar
{
    Bar() = default;
    ~Bar() = default;
    std::vector<Foo> foos;
};

b.cpp

#include "b.h"
struct Foo
{
};

c.h

#include <vector>

struct Foo;
struct Bar
{
    Bar();
    ~Bar();
    std::vector<Foo> foos;
};

c.cpp

#include "c.h"

struct Foo
{
};

Bar::Bar(){}
Bar::~Bar(){}

有错误情况的编译输出:

(py3) cpp/vector_incomplete > clang++ test.cpp b.cpp --std=c++17
In file included from test.cpp:2:
In file included from ./b.h:1:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:427:68: error: 
      arithmetic on a pointer to an incomplete type 'Foo'
        __alloc_traits::destroy(__alloc(), _VSTD::__to_raw_pointer(--__soon_to_be_end));
                                                                   ^ ~~~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:370:29: note: in
      instantiation of member function 'std::__1::__vector_base<Foo, std::__1::allocator<Foo> >::__destruct_at_end' requested here
    void clear() _NOEXCEPT {__destruct_at_end(__begin_);}
                            ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:464:9: note: in
      instantiation of member function 'std::__1::__vector_base<Foo, std::__1::allocator<Foo> >::clear' requested here
        clear();
        ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:496:5: note: in
      instantiation of member function 'std::__1::__vector_base<Foo, std::__1::allocator<Foo> >::~__vector_base' requested here
    vector() _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
    ^
./b.h:6:5: note: in instantiation of member function 'std::__1::vector<Foo, std::__1::allocator<Foo> >::vector' requested here
    Bar() = default;
    ^
./b.h:3:8: note: forward declaration of 'Foo'
struct Foo;
       ^
In file included from test.cpp:2:
In file included from ./b.h:1:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:275:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__bit_reference:16:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/algorithm:644:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:1817:55: error: 
      invalid application of 'sizeof' to an incomplete type 'Foo'
        {_VSTD::__libcpp_deallocate((void*)__p, __n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));}
                                                      ^~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:1555:14: note: in
      instantiation of member function 'std::__1::allocator<Foo>::deallocate' requested here
        {__a.deallocate(__p, __n);}
             ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:465:25: note: in
      instantiation of member function 'std::__1::allocator_traits<std::__1::allocator<Foo> >::deallocate' requested here
        __alloc_traits::deallocate(__alloc(), __begin_, capacity());
                        ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:496:5: note: in
      instantiation of member function 'std::__1::__vector_base<Foo, std::__1::allocator<Foo> >::~__vector_base' requested here
    vector() _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
    ^
./b.h:6:5: note: in instantiation of member function 'std::__1::vector<Foo, std::__1::allocator<Foo> >::vector' requested here
    Bar() = default;
    ^
./b.h:3:8: note: forward declaration of 'Foo'
struct Foo;
       ^
In file included from test.cpp:2:
In file included from ./b.h:1:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:373:52: error: 
      arithmetic on a pointer to an incomplete type 'Foo'
        {return static_cast<size_type>(__end_cap() - __begin_);}
                                       ~~~~~~~~~~~ ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:465:57: note: in
      instantiation of member function 'std::__1::__vector_base<Foo, std::__1::allocator<Foo> >::capacity' requested here
        __alloc_traits::deallocate(__alloc(), __begin_, capacity());
                                                        ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:496:5: note: in
      instantiation of member function 'std::__1::__vector_base<Foo, std::__1::allocator<Foo> >::~__vector_base' requested here
    vector() _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
    ^
./b.h:6:5: note: in instantiation of member function 'std::__1::vector<Foo, std::__1::allocator<Foo> >::vector' requested here
    Bar() = default;
    ^
./b.h:3:8: note: forward declaration of 'Foo'
struct Foo;
       ^
In file included from test.cpp:2:
In file included from ./b.h:1:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:873:54: error: 
      arithmetic on a pointer to an incomplete type 'const std::__1::vector<Foo, std::__1::allocator<Foo> >::value_type'
      (aka 'const Foo')
      __annotate_contiguous_container(data(), data() + capacity(),
                                              ~~~~~~ ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:552:9: note: in
      instantiation of member function 'std::__1::vector<Foo, std::__1::allocator<Foo> >::__annotate_delete' requested here
        __annotate_delete();
        ^
./b.h:6:5: note: in instantiation of member function 'std::__1::vector<Foo, std::__1::allocator<Foo> >::~vector' requested here
    Bar() = default;
    ^
./b.h:3:8: note: forward declaration of 'Foo'
struct Foo;
       ^
4 errors generated.

【问题讨论】:

  • 失败代码的代码列表以及编译器错误会很有用。
  • 问题应该直接包含相关代码,而不是链接后面。见How to Ask
  • stackoverflow.com/q/44664807/1116364 我的一个老问题可能有帮助吗?
  • @DanielJour 谢谢。我已经看到你的问题了。我仍然不明白为什么当我默认 ctor 并在单独的文件中定义 Foo 时出现编译错误,但在其他两种情况下却没有。
  • 想想 ~Bar 在哪里被实例化:编译器是否可以访问 Foo 在该翻译单元的任何位置的大小? (在 2 个单独的命令中编译 2 个文件可能会更清晰)

标签: c++ compiler-errors c++17 stdvector incomplete-type


【解决方案1】:

包括 b.h 将不起作用。 std::vector 模板中的某处将有一行为NUM * sizeof(Foo) 分配内存。如果把 Foo 的定义埋在源文件中,那么 Foo 的大小将是未知的,编译会失败。

【讨论】:

  • 嗯,那为什么其他两种情况都没有失败呢?
【解决方案2】:

它失败是因为你有默认的构造函数和析构函数,这意味着实现在每个编译单元中,就像你写的那样。

struct Bar
{
    Bar() {}
    ~Bar() {}
    std::vector<Foo> foos;
};

然后编译器需要知道Foo的大小并想调用它的析构函数。

但是,如果你在 .cpp 中引入函数的实现并且只在头文件中声明,你可以节省编译 main.cpp,因为编译器需要知道的一切都在那里。

你也可以(我会推荐它)默认c.cpp中的函数:

#include "c.h"

struct Foo
{
};

Bar::Bar() = default;
Bar::~Bar() = default;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-31
    • 2017-03-05
    • 1970-01-01
    • 2018-10-23
    • 1970-01-01
    • 1970-01-01
    • 2019-08-27
    • 1970-01-01
    相关资源
    最近更新 更多