Scott Meyer states in Effective C++: Item 30: Understand the ins and outs of inlining that constructors and destructors are often worse candidates for inlining.
Defining functions inside a class definition, requests (not commands) them implicitly to be inline. Depending on the quality of your compiler, the compiler decides whether or not (explicitly or implicitly) defined functions be actually inlined or not.
Taking all these into account, is it a better practice to explicitly define empty/copy/move constructors, copy/move assignment operators and destructors as default (i.e. with the default keyword) inside the body files than inside the header files? After all, default deals purely with implementation as opposed to the dual delete?
Without ever reading "Effective C++: Item 30" I can definitely tell that it makes perfect sense to define empty-looking ctors/dtors inside .cpp:
// MyClass.h:
class MyClass
{
public:
MyClass();
~MyClass();
...
}
// MyClass.cpp:
MyClass::MyClass() = default;
MyClass::~MyClass() = default;
This might look like waste for digital ink, but this is exactly how it has to be done for heavy classes that have large inheritance list or lots of non trivial members.
Why do I think it has to be done like this?
Because if you don't do that, then in every other translation unit where you create or delete MyClass compiler will have to emit inline code for entire class hierarchy to create/delete all members and/or base classes. In giant projects this is usually one of main reasons for builds that takes hours.
To illustrate, compare generated assembly with non-inline ctor/dtor and without. Not that if you have multi-level inheritance with virtual classes then amount of generated code grows very fast. Some call it C++ code bloat.
Basically if you have inline function in your class and you use that function in N different cpp files (or worse in some header files that are used by many other cpp files) then compiler would have to emit that code N times in N different object files, and then at link time merge all these N copies into one version. This rule applies basically to any other function, however, it's not very common to make large function inline in header files (because it's just bad). The issue with constructors, destructors and default assignment operators etc is that they may look like empty or no c++ code at all, while they actually need to perform that same operation recursively for all members and base classes and all of that results in very large amount of generated code.
Another use case of defining a destructor = default inside the body file, is the PImpl idiom in combination with std::unique_ptr.
header file: example.hpp
#include <memory>
// Example::Impl is an incomplete type.
class Example
{
public:
Example();
~Example();
private:
struct Impl;
std::unique_ptr< Impl > impl_ptr;
};
body file: example.cpp
#include "example.hpp"
struct Example::Impl
{
...
};
// Example::Impl is a complete type.
Example::Example()
: impl_ptr(std::make_unique< Impl >())
{}
Example::~Example() = default; // Raw pointer in std::unique_ptr< Impl > points to a complete type so static_assert in its default deleter will not fail.
At the point in the code where std::unique_ptr< Impl > is destroyed, Example::Impl must be a complete type. Therefore, implicitly or explicitly defining Example::~Example in the header file will not compile.
A similar argument applies for the move assignment operator (since the compiler-generated version needs to destroy the original Example::Impl) and for the move constructor (since the compiler-generated version needs to destroy the original Example::Impl in case exceptions).
Related
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.
As weird as the question sounds, I meant when a class is defined solely in cpp file because it is more of less a helper class for the implementation of another class and doesn't deserve to be in its private section. I understand that inlining constructor and destructors is a bad practice, but what about this situation, demonstrated as follows? Thank you very much
EDIT: I should have reworded it. In some cases inlining constructor and destructors causes bloated code unexpectedly(as discussed in Effective C++ Item 30, "Understand the ins and outs of inlining"). However I would like to know if such inlining demonstrated resulted in that as well
// my_class.cpp
#include <my_class.h>
namespace {
class Helper
{
public:
Helper() {...} // should I inline the constructor here?
~Helper() {...} // should I inline the destructor here?
};
/* alternative implementation looks like
Helper::Helper()
{...}
Helper::~Helper()
{...}
*/
} // end of anonymous namespace
// implementation of my_class
This is probably a moot point. Another discussion here discusses this to a good degree. The basic take away is that the compiler may ignore your "inline" or it may choose to "inline" the function/constructor/whatever without your input. The inline command is simply a suggestion that the compiler is free to ignore.
TL;DR Go for it; it probably isn't going to make a difference.
It's fine either way. If ever a helper function becomes a performance bottleneck because it is not inlined, you might consider making it an inline function.
Many times I find that a single instance of the Helper class is adequate for use by the main class. Hence, whether the constructor and destructor are inlined or not does not make any difference at all.
namespace {
class Helper
{
public:
Helper() {...}
~Helper() {...}
};
// The sole instance of the Helper class.
static Helper helper;
}
void main_class::foo()
{
helper.foo();
}
Suppose I have a public class and a private implementation class (e.g. PIMPL pattern), and I wish to wrap the private class with a template smart pointer class with a checked delete, as follows:
PublicClass.h
class PrivateClass;
// simple smart pointer with checked delete
template<class X> class demo_ptr
{
public:
demo_ptr (X* p) : the_p(p) { }
~demo_ptr () {
// from boost::checked_delete: don't allow compilation of incomplete type
typedef char type_must_be_complete[ sizeof(X)? 1: -1 ];
(void) sizeof(type_must_be_complete);
delete the_p;
}
private:
X* the_p;
};
// public-facing class that wishes to wrap some private implementation guts
class PublicClass
{
public:
PublicClass();
~PublicClass();
private:
demo_ptr<PrivateClass> pvt;
};
PublicClass.cpp
#include "PublicClass.h"
class PrivateClass
{
public:
// implementation stuff goes here...
PrivateClass() {}
};
//---------------------------------------------------------------------------
PublicClass::PublicClass() : pvt(new PrivateClass()) {}
PublicClass::~PublicClass() {}
main.cpp
#include "PublicClass.h"
int main()
{
PublicClass *test = new PublicClass();
delete test;
return 0;
}
This code compiles successfully on Visual C++ 2008, but fails to compile on an old version of C++ Builder. In particular, main.cpp does not compile because demo_ptr<PrivateClass>::~demo_ptr is being instantiated by main.cpp, and that destructor won't compile because it can't do sizeof on an incomplete type for PrivateClass. Clearly, it is not useful for the compiler to be instantiating ~demo_ptr in the consuming main.cpp, since it will never be able to generate a sensible implementation (seeing as how ~PrivateClass is not accessible). (PublicClass.cpp compiles fine on all tested compilers.)
My question is: what does the C++ standard say about implicit instantiation of a template class's member functions? Might it be one of the following? In particular, has this changed over the years?
If a template class is used, then all member functions of the class should be implicitly instantiated - whether used or not?
Or: template class functions should only be implicitly instantiated one at a time if actually used. If a particular template class function isn't used, then it shouldn't be implicitly instantiated - even if other template class functions are used and instantiated.
It seems clear that the second case is the case today because this same pattern is used with PIMPL and unique_ptr with its checked delete, but maybe that was not the case in the past? Was the first case acceptable compiler behavior in the past?
Or in other words, was the compiler buggy, or did it accurately follow the C++98 standard, and the standard changed over the years?
(Fun fact: if you remove the checked delete in C++ Builder, and have function inlining turned off, the project will happily compile. PublicClass.obj will contain a correct ~demo_ptr implementation, and main.obj will contain an incorrect ~demo_ptr implementation with undefined behavior. The function used will depend on the order in which these files are fed to the linker.)
UPDATE: This is due to a compiler bug, as noted by Andy Prowl, which is still not fixed in C++ Builder XE8. I've reported the bug to Embarcadero: bcc32 compiler causes undefined behavior when using std::auto_ptr with PIMPL idiom because template instantiation rules do not follow C++ spec
If a template class is used, then all member functions of the class should be implicitly instantiated - whether used or not?
No, this is definitely not the case. Per Paragraph 14.7.1/10 of the C++11 Standard (and Paragraph 14.7.1/9 of the C++03 Standard) very clearly specifies:
An implementation shall not implicitly instantiate a function template, a member template, a non-virtual
member function, a member class, or a static data member of a class template that does not require instantiation.
As for when instantiation is required, Paragraph 14.7.1/2 specifies:
Unless a member of a class template or a member template has been explicitly instantiated or explicitly
specialized, the specialization of the member is implicitly instantiated when the specialization is referenced
in a context that requires the member definition to exist; [...]
This is certainly not the case if a member function is never referenced.
Unfortunately, I can't provide an official reference on what the rules were before C++03, but to the best of my knowledge the same "lazy" instantiation mechanism was adopted in C++98.
unique_ptr is a funny beast.
Unlike auto_ptr, which called the destructor of the referenced object from the smart pointer destructor, unique_ptr::~unique_ptr merely invokes a deleter function which has been previously stored.
The deleter function is stored in the unique_ptr constructor, which is called for each and every PublicClass constructor. The user-defined constructor is defined in a context where PrivateClass::~PrivateClass is available, so that's ok. But what about other implicitly-generated PublicClass constructors, such as a move constructor? They're generated at the point of use; they also need to initialize the unique_ptr member, meaning they must supply a deleter. But without PrivateClass::~PrivateClass, they can't.
Wait, your question mentions unique_ptr, but your code isn't using it. Weird...
Even when the destructor is invoked from the smart pointer destructor, it can still be ODR-used from the containing class constructors. This is for exception-safety -- the constructor needs the capability to tear down a partially-constructed class, which includes destruction of members.
It looks like C++Builder may be generating a PublicClass copy or more constructor, even though your program doesn't use it. That doesn't fall afoul of the rule Andy mentioned, because PublicClass isn't a template. I think the compiler is entitled to generate a defaulted copy constructor for PublicClass when it processes the class definition, since you can't provide an out-of-class definition for a defaulted member. Respecting the rule of three for demo_ptr will preclude PublicClass having a copy constructor, and therefore may solve your problem.
When I use the pimpl idiom, is it a good idea to put all the methods definitions inside the class definition? For example:
// in A.h
class A {
class impl;
boost::scoped_ptr<impl> pimpl;
public:
A();
int foo();
}
// in A.cpp
class A::impl {
// method defined in class
int foo() {
return 42;
}
// as opposed to only declaring the method, and defining elsewhere:
float bar();
};
A::A() : pimpl(new impl) { }
int A::foo() {
return pimpl->foo();
}
As far as I know, the only problems with putting a method definition inside a class definition is that
(1) the implementation is visible in files that include the class definition, and
(2) the compiler may make the method inline.
These are not problems in this case since the class is defined in a private file, and inlining has no effect since the methods are called in only one place.
The advantage of putting the definition inside the class is that you don't have to repeat the method signature.
So, is this OK? Are there any other issues to be aware of?
I think you answered your own question : both solutions are equivalent.
However, I wouldn't be so sure that 'inlining has no effect since the methods are called in only one place' : an additional call could exists when the functions are not inlined. But chances are that the compiler is smart enough to optimize them away from the one-line forwarding calls in the outer class.
In the end, I believe it's just a matter of taste.
Advantages:
all code of the class is localized
Disadvantages:
for larger classes: when scrolling is needed, it becomes more difficult to know to which class the function belongs.
dependencies are more easily solved when functions reside after all class declarations. Otherwise, it might be needed that some class declarations are moved after others and some functions still have to be moved after the class declaration when there are mutual dependency of internal classes.
Usually I don't add methods to the Impl inner class, but I can't see any issue if you define the methods inline. It seems to me much more readable than having seperate declaration and definition.
Whether the compiler inlines the methods depends on the compiler and the passed parameters.
In the case of the pimpl idiom, I don't think it matters whether the methods are defined within the Imp's body or not. I personally like them defined outside, because it is easy to see what is really important (like member variables and list of methods).
It is possible to declare a class without defining it (forward declaration) as long as it is defined later on within the translation unit. In the case of functions, one can declare a function without defining it within the translation unit, and the linker will link it to its definition in a different translation unit. Is it possible to do the same with class declarations?
(if this is not possible, is there any use to a forwardly declared class without a definition in the current TL, or is that always an error?)
something like this, except this doesn't compile:
mymain.cpp:
class myclass; // declare without defining
myclass::myclass();
void myclass::barf();
int main() {
myclass *m = new myclass();
m->barf();
return 0;
}
myclass.cpp:
#include <iostream>
class myclass { // define the implementation
public:
myclass();
void barf();
};
myclass::myclass() { } //empty constructor
void myclass::barf() {
std::cout << "barfing\n";
}
It is possible to forward-declare a class, but only pointers and references to forward-declared classes can be used. You can't use an actual object of a forward-declared class because the compiler doesn't know enough about it; in particular it doesn't know how large the object is or what its members are. Trying to forward-declare member functions (as you have done) won't work because the syntax of the forward declaration doesn't allow you to specify whether the functions are virtual or non-virtual (or perhaps inherited from some base class).
It is not often useful to forward-declare a class in a source file, but it can be useful in a header file. In particular it's common to forward-declare a class in a library's public header file and use pointers to that type as opaque handles. The class definition remains private to the library but user code can pass around pointers to objects of that class without ever knowing what the class's implementation looks like. This works particularly well when the pointers are smart pointers.
You can, but only if you use exclusively pointers or references to that class. But you can't use code referring to that class' members (variables or methods). You can only use it to declare pointer variables to that class.
I would suggest you create a myclass.h header file with myclass' full declaration, and include that header in mymain.cpp.
You can only do that through hacks.
The declare before use rule doesn't hold within a class (see here).
Otherwise you can do that by declaring your function as a template function, whose template parameter is of myclass (in your example).
The only non-hack way is to define the class (ie. by including the header file its defined in).