First some background. As the C++17 standard says:
[vector.overview]/3 An incomplete type T may be used when instantiating vector if the allocator satisfies the allocator completeness requirements 17.6.3.5.1. T shall be complete before any member of the resulting specialization of vector is referenced.
I have tried 3 scenarios in this repo (code copied at the bottom):
- The class containing the incomplete vector type is declared (default ctor/dtor) and defined in the same header file << Compilation Succeeds
- compiling with a.h included:
clang++ test.cpp --std=c++17
- compiling with a.h included:
- The class containing the incomplete vector type is declared (default ctor/dtor) and defined in header and source files << Compilation Fails
- compiling with b.h included:
clang++ test.cpp b.cpp --std=c++17
- compiling with b.h included:
- The class containing the incomplete vector type is declared and defined in header and source files (ctor/dtor explicitly defined) << Compilation Succeeds
- compiling with b.h included:
clang++ test.cpp c.cpp --std=c++17
- compiling with b.h included:
My question is, why does the compilation fail in the second case but not the first or the third? If as the standard says a member of std::vector is referenced, how come it is not referenced in the header only case? And what is this referenced member? Is there any way I can have the second case compile without touching the default keyword or the forward declaration?
P.S. I have tried with clang 9.0.0 and Apple clang version 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(){}
Compilation output for the case with error:
(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.