The code below compiles, even though I was expecting the compiler to complain that it doesn't know the size of Foo. However, if I replace the #include with a forward declaration of Bar, it doesn't compile for this reason.

I know that Foo is definitely only forward declared because if Foo* myfoo; is changed to Foo myfoo; then it does not compile. Why does this error only occur with a vector of objects of one type, but not the other?

Using Visual Studio Express 2013.

#include "Bar.h"
class Foo;

class MyClass{
    Foo* myfoo;
    std::vector<Foo> foos;
    std::vector<Bar> bars;
};
有帮助吗?

解决方案

In and of themselves, there is no reason that template type arguments need to be complete types (in fact, 14.3.1/2 explicitly states that this is not the case).

Such a requirement then comes from how that type is used within the definition of your type template (in this case, std::vector). For example, [C++11: 20.7.1/5] states that the template argument T of a std::unique_ptr<T> may be incomplete at the point of the unique_ptr's declaration; this is stated explicitly because it was not the case for std::auto_ptr.

You'll find that as soon as you try to do pretty much anything with your vector, you'll need that T to be complete. This issue is detectable at compile-time as it all comes down to the instantiation of templates, so your compiler will do the appropriate erroring as and when required.

The vector's destructor is usually one of those things.

So, using GCC 4.8, I cannot instantiate either vector with an incomplete value_type:

#include <vector>

class Foo;
class Bar;

std::vector<Foo> foos;
std::vector<Bar> bars;

int main() {}

error: invalid use of incomplete type 'class Foo'
error: invalid use of incomplete type 'class Bar'

Only when I use them as members in a class that never gets used is the entire thing compilable, because the vectors' destructors are never invoked and therefore never template-instantiated:

#include <vector>

class Foo;
class Bar;

class T
{
   std::vector<Foo> foos;
   std::vector<Bar> bars;
};

int main() {}

(no error)

Whatever your implementation does, it'll be the same for both Foo and Bar; if you're seeing a difference between them, then you must be doing something different with Bar that you have not shown us.

其他提示

(I know its already answered, but I'm going to post mine any way)

A good way to think of this is, when does the definition of the class need to be known? In the case of vector<Foo>, the sizeof(Foo) needs to be known at reserve time, or access time, and the Foo::constructor and Foo::destructor information needs to be known when we add or remove items to the vector. And of course, it needs the Foo::destructor when the vector is being destroyed.

So this leads to one of the more common problems with forward declared template arguments to std::vector: you have a class above that uses a default constructor and a default destructor. When is the default destructor defined? Well (semantically, atleast) its defined when you don't define it in the class, so it is defined in this header file. More to the point, what is in that destructor? Hidden in every C++ destructor is clean up code that goes beyond the body of the destructor: it calls all the destructors of all members...but that means it tries to call the destructor for std::vector.

Are you SOL? Nope. The following should work just fine for you.

//myclass.hpp
class Foo;
class Bar;
class MyClass{
public:
    MyClass();
    ~MyClass();
    Foo* myfoo;
    std::vector<Foo> foos;
    std::vector<Bar> bars;
private:
};

//myclass.cpp
#include "myclass.hpp"
#include "Bar.h"
#include "Foo.h"
MyClass::MyClass(){};
MyClass::~MyClass(){};
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top