可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a header file like the below -
// abc.hpp #include <vector> #include <string> namespace A { namespace B { struct abc { std::string _type; }; using abc_vector = std::vector<abc>; } }
I am using forward declaration in another header file.
// con.hpp #include <vector> namespace A { namespace B { struct abc; // Forward Declaration using abc_vector = std::vector<abc>; } namespace C { class N { public: B::abc_vector foo(std::string type); }; } }
What really confuses me is that my code compiles and works.
How is the vector allowed to be declared with incomplete type..? I think that it shouldn't be able to decide the size of abc.
using abc_vector = std::vector<abc>;
The below is the code I used to test my header files. Strange enough, that it compiles and works all fine.
#include "con.hpp" #include "abc.hpp" #include <iostream> namespace A { namespace C { B::abc_vector N::foo(std::string type) { B::abc a; a._type = type; B::abc_vector d; d.push_back(a); return d; } } } int main() { A::C::N n; auto container = n.foo("test"); for (const auto& i : container) std::cout << i._type << ' '; return 0; }
回答1:
This is an interesting topic (at least to me) and applies to other std containers.
Originally the standard made it undefined behaviour to instantiate a container of an incomplete type. However implementations did not disallow it. This was in all likelihood not deliberate, but merely a side-effect of the fact that elements in (for example the vector) are stored in a memory location that is referenced by a pointer.
Thus the size of an element does not need to be known until an element is actually required - during the instantiation of a member function of the vector.
Here is a starting point for research if you'd like to explore further:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4056.html
回答2:
The code line
using abc_vector = std::vector<abc>;
only introduces a type alias for std::vector<abc>
. That doesn't require, by any means, the size of abc
since no object of type abc
is allocated at all. Only a new type is declared.
B::abc_vector d;
Indeed needs the definition of abc
. Nevertheless it works because at this point abc
already has been defined because the header file abc.hpp
has been included.
You are referring to this answer, where
std::vector<B> v;
is "done." This is not the same as what you did. You just introduced a type alias. std::vector<B> v;
actually defines a variable. Therefore the definition of B
is mandatory.
Note that
using abc_vector = std::vector<abc>;
is equivalent to
typedef std::vector<abc> abc_vector;
Maybe this makes it a bit clearer why the size of abc
isn't necessary to know at this time point in compilation.
回答3:
There is an interesting observation. Both GCC5.2
and CLANG3.6
compile following code.
struct A; std::vector<A> my_func(); //Definition of my_func is in some CPP file
But throw errors for
struct A; std::vector<A> v;
And reasoning for this is size of vector won't change for different type it is holding. See following code snippet.
struct B{int i; int j;}; struct C{int a,b,c;}; std::vector<B> pp; std::vector<C> qq; int main() { std::cout<<sizeof(pp)<<'\n'; std::cout<<sizeof(qq)<<'\n'; }
Output
24 24
But for std::vector<A> v
it has to provide the Allocator<A>()
as well. And allocator required members of struct A
like constructor, copy constructor, destructor etc. Also one important thing to note here is pointer arithmetic for incomplete type is not allowed.
If you see the errors thrown by CLANG, it clearly says same.
In file included from /tmp/gcc-explorer-compiler115920-68-1xsb8x7/example.cpp:2: In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/vector:64: /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/bits/stl_vector.h:161:9: error: arithmetic on a pointer to an incomplete type 'A' - this->_M_impl._M_start); } ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/bits/stl_vector.h:253:7: note: in instantiation of member function 'std::_Vector_base<A, std::allocator<A> >::~_Vector_base' requested here vector() ^
Everything else is quite straight forward.
Following is a typedef , so compiler does need to know about size.
using abc_vector = std::vector<abc>;
So, the code structure discussed in question is good to go ahead with.