Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaration of std::vector works with forward declared classes?

Tags:

c++

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;
}
like image 954
badola Avatar asked Mar 14 '23 10:03

badola


2 Answers

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.

like image 69
cadaniluk Avatar answered Apr 09 '23 00:04

cadaniluk


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

like image 33
Richard Hodges Avatar answered Apr 08 '23 23:04

Richard Hodges