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.
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.
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(){};
// main_pimpl_sample.cpp
#include "pimpl_sample.hpp"
using namespace std;
int main()
{
pimpl_sample p;
return 0;
}
// pimpl_sample.cpp
#include "pimpl_sample.hpp"
struct pimpl_sample::impl {
};
pimpl_sample::pimpl_sample()
: pimpl_(new impl) {
}
// pimpl_sample::~pimpl_sample()
// cause problem if missed
// {}
// pimpl_sample.hpp
#if !defined (PIMPL_SAMPLE)
#define PIMPL_SAMPLE
#include <boost/scoped_ptr.hpp>
class pimpl_sample {
struct impl;
boost::scoped_ptr<impl> pimpl_;
public:
pimpl_sample();
//~pimpl_sample(); cause problem if missed
void do_something();
};
#endif
~/Documents/C++/boost $ g++ --version
g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
~/Documents/C++/boost $ g++ -o main_pimpl_sample main_pimpl_sample.cpp pimpl_sample.cpp pimpl_sample.hpp
In file included from /usr/include/boost/smart_ptr/scoped_ptr.hpp:15:0,
from /usr/include/boost/scoped_ptr.hpp:14,
from pimpl_sample.hpp:6,
from main_pimpl_sample.cpp:2:
/usr/include/boost/checked_delete.hpp: In function ‘void boost::checked_delete(T*) [with T = pimpl_sample::impl]’:
/usr/include/boost/smart_ptr/scoped_ptr.hpp:80:9: instantiated from ‘boost::scoped_ptr<T>::~scoped_ptr() [with T = pimpl_sample::impl]’
pimpl_sample.hpp:8:20: instantiated from here
/usr/include/boost/checked_delete.hpp:32:58: error: invalid application of ‘sizeof’ to incomplete type ‘pimpl_sample::impl’
/usr/include/boost/checked_delete.hpp:32:58: error: creating array with negative size (‘-0x00000000000000001’)
The solution to above compilation error is to manually provide a destructor. The indicated reason is as follows:
you must still remember to define the destructor manually; the reason
is that at the time the compiler generates an implicit destructor, the
type impl is incomplete, so its destructor isn't called.
Question> I still have difficulties to absorb the above idea and would like to know a little detail why we have to provide a manual destructor here.
Thank you
TL;DR Declare an explicit destructor and implement it in a code module (not in the header file).
If you don't create the destructor, then the compiler creates an empty automatic destructor in every translation unit which tries to destroy an object of this class. You'd get equivalent behaviour if you defined an empty inline destructor in the class header.
This causes an error because the destructor is also responsible for calling destructors of all the class' fields, which - in order - needs the instantiation of the method template boost::scoped_ptr<impl>::~scoped_ptr();. This template, in turn, cannot be instantiated then because it tries to delete an object of type impl, which is only forward declared in that scope (and you need a full definition to know how to delete this object.
OTOH, if you declare the non-inline constructor in the header, its code is only generated in pimpl_sample.cpp, where also lies the definition of impl, so the scoped_ptr's destructor can be instantiated successfully.
Other translation units then only call pimpl_sample's destructor as an external method, so they don't need to generate it and instantiate scoped_ptr's destructor on their own.
This is excerpt from google's c++ coding guidelines.
How can we use a class Foo in a header
file without access to its definition?
We can declare data members of type Foo* or Foo&.
We can declare (but not define) functions with arguments, and/or
return values, of type Foo. (One
exception is if an argument Foo or
const Foo& has a non-explicit,
one-argument constructor, in which
case we need the full definition to
support automatic type conversion.)
We can declare static data members of type Foo. This is because static
data members are defined outside the
class definition.
What I'm curious about is exception in the second bullet. Why is this so? Why is the full definition needed if we want to support automatic type conversion?
My guess is that compiler needs the full definition of the destination type because of the temporary object that is created in the implicit conversion. Am I guessing correctly? Is there more to it?
EDIT:
As I see it, the exception in the guideline is addressed to situation like this:
class A
{
public:
A( int );
};
class B
{
public:
B( A const &a );
};
int main()
{
B b(2);
}
Here we have only one user-defined implicit conversion (from int to A), and call to constructor that accepts A const &. Only thing that makes sense in this exception is to support direct conversion from e.g. int to A, and then to B via constructor that accepts A const &, allowing client code to use this conversion chain without need to explicitly include header file where A class is declared.
The C++ language doesn't differentiate between code in header files and other file. It does not even require that a header is a file. So purely technically the question is meaningless, but in practice you restrict what you do in header files so as not to run afoul of the One Definition Rule. Without restricting yourself, users would have to be careful to only include the header file in one translation unit. With proper restrictions, the header file can be freely included in multiple translation units.
An incomplete type is one where the size is not known, where sizeof cannot be used.
When the class definition is not known, class Foo is necessarily incomplete.
This means you cannot do things that requires the size to be known. And since incompleteness means that members are not known (they would necessarily be known if the size was known) you can't generally call any members. Exception: you can call the destructor, like in delete pFoo, and the compiler must accept that, but it's Undefined Behavior if class Foo has a non-trivial destructor.
The exception noted in the Google guidelines is, however, meaningless.
EDIT: I discovered that people on SO like it better when things are spelled out in detail, so, adding discussion of why the guideline is meaningless.
The guideline says you can "declare (but not define)" but that "one exception is if an argument Foo or const Foo& has a non-explicit, one-argument constructor".
The declaration does not have anything to do with constructors, which one can affirm by simply trying it out:
#include <iostream>
struct Foo;
Foo bar( Foo const& ); // Declaration of function bar, works fine.
struct Foo
{
int x_;
Foo( int x ): x_( x ) {} // Converting constructor.
};
int main()
{
std::cout << bar( 42 ).x_ << std::endl;
}
Foo bar( Foo const& foo ) { return foo; }
In conclusion, again, the Google guidelines' exception is meaningless.
Cheers & hth.,
Suppose that foo.h knows about Foo declaration only
//foo.h
class Foo;
void f(const Foo &); // It is possible to use the reference.
Full definition is in foo.cpp
// foo.cpp
class CanBeConvertedToFoo;
class Foo
{
Foo (const CanBeConvertedToFoo & x); // implicit constructor
}
class CanBeConvertedToFoo is implicit convertable to Foo;
But it is unknown in some.cpp.
// some.cpp
#include "foo.h"
void g(const CanBeConvertedToFoo & x) {
f(x); // Is it known about implicit conversion ?
}
I don't know whether the exception in the second point is true. Implicit conversions must be know only when a function is called, not when it is declared, so the following works even though C is incomplete while f is declared:
#include <iostream>
class C;
void f(C);
struct C { C(int i) { std::cout << "C(" << i << ")" << std::endl; } };
void f(C c) { std::cout << "f(C)" << std::endl; }
int main() { f(2); }