4

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;
}
badola
  • 820
  • 1
  • 13
  • 26
  • 2
    _"I think that it shouldn't be able to decide the size of abc."_ What makes you think it needs to? – Lightness Races in Orbit Oct 19 '15 at 11:45
  • 1
    I think that works, because the type is the defined by the time you use it in a function. – Klemens Morgenstern Oct 19 '15 at 11:47
  • I read it in an answer here - [link] (http://stackoverflow.com/questions/37346/why-cant-a-forward-declaration-be-used-for-a-stdvector) **The compiler needs to know how big "B" is before it can generate the appropriate layout information.** – badola Oct 19 '15 at 11:48
  • 1
    @badola Hint: the size of an `std::vector` is independent from the size of `T` and the number of elements in the vector. – Shoe Oct 19 '15 at 11:49

3 Answers3

3

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.

Community
  • 1
  • 1
cadaniluk
  • 15,027
  • 2
  • 39
  • 67
2

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

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Thanks @Richard Hodges, this was a much more convincing answer. :) Can you point to more articles, where I can read about it..? – badola Oct 19 '15 at 17:28
  • 1
    Hmm well your question prompted me to remember something I read in the boost documentation some time ago. This link contains a fairly complete narrative http://www.boost.org/doc/libs/1_55_0/doc/html/container/main_features.html#container.main_features.containers_of_incomplete_types – Richard Hodges Oct 19 '15 at 17:40
2

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.

g-217
  • 2,069
  • 18
  • 33
  • I am curious, will you please explain why does the size of - std::vector pp; std::vector qq; Both return 24. – badola Oct 20 '15 at 08:11
  • Nevermind, I got the explanation here - http://stackoverflow.com/questions/2373189/sizeof-a-vector – badola Oct 20 '15 at 08:13