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(){};
Related
I got a small problem which i don't understand for now and can't find explanation for it. I read about how to use std::unique_ptr in PIMPL idiom and it works but.. not in one weird situation, which of course occured to me now.
The simplest - i will show a simplified code example which reproduce a problem (Compiling with VS2017 Community).
header.h ##
Forward declaration of Forward class, and template class TestForward which has virtual function returning unique_ptr.
class Forward;
using TestUniquePtr = std::unique_ptr<Forward>;
TestUniquePtr make_ptr();
template<int a>
class TestForward {
public:
virtual TestUniquePtr foo();
};
template<int a>
TestUniquePtr TestForward<a>::foo() {
return make_ptr();
}
forward.h
#include "header.h"
#include <iostream>
class Forward {
public:
~Forward() {
std::cout << "HAAA" << std::endl;
}
};
forward.cpp
#include "forward.h"
TestUniquePtr make_ptr() {
return TestUniquePtr{ new Forward };
}
main.cpp
File which does not compile due to 'can't delete an incomplete type'.
Notice that function foo is not even called in here.
So compiler should try to compile this function in this unit?
If this function is not virtual or TestForward is not a template - it works.
#include "header.h"
int main (int argc, char *argv[]) {
TestForward<3> a;
return 0;
}
I know how can i fix this - by defining my deleter which is not a template, and write its definition in forward.cpp but.. I think this should work, so please help me find out why template+virtual make it not working :(
There is so much going on here that all plays together into this error ...
First, consider this: the C++ standard says that if you do this:
struct Incomplete;
void foo(Incomplete* p) { delete p; }
this is legal, but if the full definition of Incomplete turns out to have a non-trivial destructor, the program has undefined behavior. I believe this solely for compatibility with early C-like C++ programs.
So, to improve the safety of programs, the default deleter of unique_ptr uses a "safe delete", i.e. one that fails to compile for incomplete types. This means that the instantiation of the unique_ptr destructor must be aware of the full definition of the pointed-to class.
In your program, any code that uses the TestUniquePtr destructor must therefore be aware of the full definition of Forward.
TestForward::foo uses the destructor. make_ptr returns an object. foo move-constructs its own return value from this object, and then destroys the source. (In the actual generated code, this is most likely optimized away by the return value optimization, but the code must still be valid without it.)
And where/why is TestForward<3>::foo used? Well, since it is virtual, it must be instantiated whereever the vtable of the class is instantiated. And since it is a template instantiation, the vtable is instantiated wherever the constructor is called (because the constructor needs to write the vtable pointer to the object). And the constructor is called in main.
If foo is not virtual, there's no need to instantiate it. And if TestForward is not a template, I guess you put foo into some separate source file instead of the header, so the error didn't manifest for main.
So how do you fix this?
In a typical Pimpl context, you fix this by tightly controlling who instantiates the destructor of the unique_ptr. You explicitly declare the destructor of the interface class and put the definition into the source file where the impl class definition is known.
If, however, you want to hand out unique_ptrs to your incomplete class as opaque handles, you need to replace the default deleter.
// header.h
class Forward;
struct ForwardDeleter {
void operator ()(Forward* ptr);
};
using TestUniquePtr = std::unique_ptr<Forward, ForwardDeleter>;
// forward.cpp
class Forward { ... };
void ForwardDeleter::operator ()(Forward* ptr) { delete ptr; }
I came about the same issue as described here
Can't allocate class with forward declared value in std::map member variable
in our codebase.
Hoever I also found other cases where our compiler (MSVC 2017) is able to compile this...
After fiddling around with the code I found that defining the con- & destructor in the cpp allows the files to compile.
In test.h:
#ifndef TEST_H
#define TEST_H
#include <map>
struct Incomplete;
class Test {
std::map<int, Incomplete> member;
public:
Test();
~Test();
int foo() { return 0; }
};
#endif
In test.cpp:
#include "test.h"
struct Incomplete {};
Test::Test() {}
Test::~Test() {}
In main.cpp:
#include "test.h"
int main()
{
Test test;
return test.foo();
}
Why does defining the con- & destructor in the cpp file allow member-std::map-variables to use incomplete types?
This is because declaring the class member does not require Incomplete to be complete, but invoking the std::map destructor does, because it necessarily needs to invoke the Incomplete destructor to destroy the map contents. (Invoking the default std::map constructor could require the type to be complete, depending on the implementation. I'm not sure if the spec puts any requirements on this. I can think of at least one implementation that would not require complete types.)
If you rely on the compiler to generate implicit ctors/dtors for you, that means that the type must be complete when the class definition is encountered, because that's when the compiler is going to implicitly generate the ctor and dtor. It's as though you wrote inline Test::Test() {} inline Test::~Test() {} immediately following the class definition. The dtor is implicitly going to destroy the map, which is going to destroy the map contents by calling ~Incomplete() on any stored values, which we can't do without a definition for Incomplete. And there, the whole thing falls apart and you get an error.
However, if you tell the compiler (by way of the Test ctor/dtor declarations) that you will be implementing them later, then it won't generate them, therefore no std::map ctor/dtor invocation gets compiled at that point.
Then you complete the Incomplete type prior to defining the ctor/dtor yourself, so the Incomplete ctor/dtor invocations can be successfully compiled. If you remove the definition of Incomplete then you will run into the same error.
Note that, as others have said, you can side-step this issue by storing pointers/references to the incomplete type in the map instead. A pointer or reference to an incomplete type is actually itself a complete type. However, this may not be desirable in all cases so I'm hesitant to push that solution without knowing more details about how the map will be used.
I have two classes say foo and bar. The constructor of foo is private so as to only allow the bar class to instantiate it.
// foo.h
class foo
{
friend class bar;
private:
foo()
{}
};
// bar.h
class bar
{
public:
bar()
{
foo* f = new foo();
}
};
Also I am using boost::shared_ptr however for simplicity I did not mention it here
However when I attempt to build the program I get the error
In instantiation of ‘typename boost::detail::sp_if_not_array::type
boost::make_shared() [with T = bar; typename
boost::detail::sp_if_not_array::type = boost::shared_ptr]’:|
/home/..../Projects/CodeBlocks/../bar.cpp|18|required from here|
/home/..../Projects/CodeBlocks/../foo.h|23|error: ‘foo::foo()’ is
private| ||=== Build finished: 1 errors, 2 warnings (0 minutes, 5
seconds) ===|
Is it because bar.h gets built first? If so, any suggestions on how to fix this?
I am using boost::shared_ptr however for simplicity I did not mention it here
Ironically, that is the very thing that's making your code fail to compile. Without it, the example you've shown compiles without errors.
The problem is that you're trying to use boost::make_shared to create the shared_ptr. This will lead to make_shared attempting to construct a foo, which of course fails, because you've declared bar to be a friend of foo's, but make_shared isn't a friend.
Use the shared_ptr constructor directly instead, and pass it a pointer to a foo object that has been allocated by bar.
Live demo
Note that declaring boost::make_shared as a friend is not a reliable solution either because it may delegate the actual construction to some other function.
I don't understand the purpose of boost::checked_delete. The documentation says:
The C++ Standard allows, in 5.3.5/5,
pointers to incomplete class types to
be deleted with a delete-expression.
When the class has a non-trivial
destructor, or a class-specific
operator delete, the behavior is
undefined. Some compilers issue a
warning when an incomplete type is
deleted, but unfortunately, not all
do, and programmers sometimes ignore
or disable warnings.
The supplied function and class
templates can be used to prevent these
problems, as they require a complete
type, and cause a compilation error
otherwise.
So the C++ standard allows you to delete incomplete types, which causes undefined behavior if the type has a non-trivial destructor. What? How can an incomplete type have any destructor at all? Isn't an incomplete type just a prototype?
The most common example of an incomplete type is one that has only been declared:
// this file does not include the definition of foo
class foo;
void bad(foo *f)
{
delete f; // undefined behavior if there exists foo::~foo
}
In reality, the definition of foo may look like this:
class foo
{
public:
~foo() { ... };
};
But if the top code has not 'seen' the class definition and just sees the class declaration, the code will compile.
Consider the following:
Foo.h:
#ifndef Foo_H
#define Foo_H
#include <boost/scoped_ptr.hpp>
#include <boost/utility.hpp>
class Foo : private boost::noncopyable
{
public:
Foo();
~Foo();
void do_something_interesting();
private:
class Impl; // incomplete type
boost::scoped_ptr<Impl> impl;
};
#endif
Foo.cpp:
#include "Foo.h"
#include <string>
#include <iostream>
class Foo::Impl
{
public:
Impl() : name("Foo::Impl")
{}
void say_hi()
{ std::cout << name << " says hi!" << std::endl; }
std::string name;
};
Foo::Foo()
: impl(new Impl)
{}
Foo::~Foo()
{}
void Foo::do_something_interesting()
{ impl->say_hi(); }
Given this (contrived) example, you cannot inline either Foo::Foo or Foo::~Foo because the type is incomplete. By defining both in a context where the type Foo::Impl is a complete type, you can safely delete the type. boost::checked_delete does this safety check for you, and it's purely a compile-time cost. If you either inline Foo::~Foo or omit it entirely, you will get an error from boost::checked_delete wherever you try to destroy a Foo instance.
C++ allows you to use delete on variables that at the time are pointers to incomplete types.
struct S; // incomplete
int main() {
S* s = NULL;
delete s; // legal
}
The compiler doesn't know at that point what S really is. If it turns out S has a non-trivial destructor, then the compiler is not required to detect that problem.
Practically speaking, what probably happens is that when the compiler encounters the delete instruction on an incomplete type, it fills in a call to what it expects will be the type's ordinary compiler-generate default destructor. And if that's what the destructor turns out to be, then everything's fine. But if it turns out that S has a non-trivial destructor, or if it provides its own special method of deletion, then what the compiler filled in earlier will be wrong. The compiler, however, allowed to assume that it correctly compiled the delete instruction and never look back. When that assumption is wrong, you'll get undefined behavior.
The Boost function ensures that it's called only on complete types, thus avoiding the undefined behavior that may occur on incomplete types.
Is following code legal C++ or not?
class Foo
{
class Bar;
void HaveADrink(Bar &bar);
void PayForDrinks(Bar &bar);
public:
void VisitABar(int drinks);
};
class Foo::Bar
{
public:
int countDrinks;
};
void Foo::HaveADrink(Bar &bar)
{
bar.countDrinks++;
}
void Foo::PayForDrinks(Bar &bar)
{
bar.countDrinks = 0;
}
void Foo::VisitABar(int drinks)
{
Bar bar;
for (int i=0; i<drinks; i++) HaveADrink(bar);
PayForDrinks(bar);
}
Both Visual C++ and GCC accepts it, however the code seems somewhat strange to me and I would hate to have it refused by some future compiler.
Still, the pattern seems useful to me to reduce compile time dependencies - I often use it to declare structs which are used to pass some "context" (a bunch of variables) which are shared between a few functions which all reside in the same cpp file, and this way I do not have to introduce the "context" definition into the public interface.
legal, and indeed usefull to hide implementation details to the outside world.
[edit]
I originally said this was the "pimpl idiom" : http://c2.com/cgi/wiki?PimplIdiom
but I agree that this is just part of pimpl is about. This technique is used by pimpl.
You're "forwarding" class Bar inside of class Foo. Perfectly legal as long as you don't do anything inside the definigino of Foo that would require the sizeof Bar. You can reference Bar using pointer or reference (Bar* or Bar&), but if you declare a data member in Foo such as this:
private:
Bar _bar;
It wouldn't work. The reason is because the definition of Foo must be enough to determine the sizeof Foo. Since the size of Bar is unknown inside the definition of Foo, it would make the size of Foo indeterminate. But using a pointer would work:
private:
Bar* _bar;
Because the sizeof of pointer is the same, and thus known, regardless of how Bar will be later defined.