Pimpl with smart ptr - Why constructor/destructor needed [duplicate] - c++

This question already has answers here:
std::unique_ptr with an incomplete type won't compile
(7 answers)
Closed 9 years ago.
Lets consider following example (using c++11)
A.hpp:
#include <memory>
class A
{
public:
//A();
//~A();
private:
struct AImpl;
std::unique_ptr<AImpl> pImpl;
};
main.cpp:
#include "A.hpp"
int main()
{
A a;
}
Using default constructor and destructor. Does not compile. Following error occurs:
In file included from /usr/include/c++/4.8/memory:81:0,
from A.hpp:2,
from main.cpp:2: /usr/include/c++/4.8/bits/unique_ptr.h: In instantiation of 'void
std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp =
A::AImpl]': /usr/include/c++/4.8/bits/unique_ptr.h:184:16: required
from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = A::AImpl;
_Dp = std::default_delete]' A.hpp:3:7: required from here /usr/include/c++/4.8/bits/unique_ptr.h:65:22: error: invalid
application of 'sizeof' to incomplete type 'A::AImpl'
static_assert(sizeof(_Tp)>0,
A similar error occurs when using boost::scoped_ptr instead of std::unique_ptr. Do I understand this correctly - it means, that the forward declaration of AImpl is not enough?
When adding constructor and destructor, everything works fine. What is the reason? Is it because the defaults are inlined an thus do not see the size of AImpl? And when adding constructor and destructor, the compiler assumes, that those definitions know the size of AImpl?

The unique_ptr destructor needs to know the full definition of AImpl, because it deletes it. So the question is, where does the unique_ptr destructor sit? It's a template, so the question is about the instantiation point.
The destructor is instantiated when it is first used. Both the constructor and the destructor of the containing class use it (the constructor needs it if its body throws an exception). So the unique_ptr destructor is instantiated where A's constructor or destructor are placed, whichever comes first.
If you default those special members, they are generated immediately after the class body, i.e. in the header, where the size of AImpl is not known.
If you instead declare them in the class and then put definitions (you may =default those definitions) in the .cpp, after the full definition of AImpl, then the unique_ptr destructor is instantiated there.

Related

Call to implicitly deleted copy constructor of class error [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have a class that has a member of type std::thread, however, when I want to use this class as a member of another class I always get the error that the constructor of the other class was implicitly deleted because std::thread constructor is deleted.
My class with the std::thread member looks as follows:
class VideoSender {
public:
VideoSender() : encoderThread([]{}) {};
~VideoSender(){};
private:
std::thread encoderThread;
};
The other class looks like this:
class RemoteCom {
public:
RemoteCom() : videoSender() {};
~RemoteCom(){};
private:
VideoSender videoSender;
};
I was thinking that I already am properly initializing the std::thread class with (here an empty) function. But I still do get this error, so it seems like somewhere there is still a copy constructor called?
I should also add the I can use the VideoSender class directly without errors. This only happens when used as a member of another class.
Edit
I then initialize RemoteCom like this:
RemoteCom com = RemoteCom();
This is really all there is. I reduced it all to this minimal example, the error still exists.
The syntax
RemoteCom com = RemoteCom();
requires a copy constructor to exist and to be accessible. This is true even if optimization would eliminate the call to the copy constructor.
Replace with
RemoteCom com;
which uses the default constructor directly.
Like you have already noticed, std::thread is not copiable, which is why your class' implicit copy constructor is deleted. For this declaration the compiler could utilize the move constructor
RemoteCom com = RemoteCom();
but it cannot, as it is also deleted. C++ standard section 11.4.5.3 §8:
If the definition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if
X does not have a user-declared copy constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared move assignment operator, and
X does not have a user-declared destructor.
As the VideoSender requires a user-declared destructor, because the thread has to joined before it is destructed, I would just declare the move operations explicitly defaulted:
class VideoSender {
public:
VideoSender() = default;
~VideoSender() {
// a moved thread is not joinable
if (encoderThread.joinable()) { encoderThread.join(); }
}
VideoSender(const VideoSender&) = delete;
VideoSender& operator=(const VideoSender&) = delete;
// Move constructor and move assignment operator
// Transfer ownership of the thread to this object
VideoSender(VideoSender&&) noexcept = default;
VideoSender& operator=(VideoSender&&) noexcept = default;
private:
// Brace initializer
std::thread encoderThread{ []{} };
};
For the RemoteCom I would just refer to the rule of zero (C++ Core Guidelines C.20):
class RemoteCom {
// Implicitly declared special member functions are all ok
private:
VideoSender videoSender;
};
With the declarations above the following code would compile on C++14 and C++11:
#include <utility>
// Move construction (may be elided)
RemoteCom com = FunctionReturningARemoteCom();
// Move assignment operator
com = FunctionReturningARemoteCom();
// Move construction
Remote com2 = std::move(com);
With C++17 things are a little bit different because of something called guaranteed copy elision, but that is outside of this answer.

Why is a forward declaration not enough even if the data type is never used [duplicate]

This question already has answers here:
When can I use a forward declaration?
(13 answers)
Closed 5 years ago.
I know that similar questions have already been asked and there are answers describing that in the situations describe below forward declaration is not enough. However, these answers do NOT tell WHY it is not enough. Therefore, I don't think that this is a duplicated question.
I have some difficulties to understand why the compiler has to see the definition of a data type (a foward declaration is not sufficient) in the following situation:
Let's consider we have a simple "main.cpp" as shown below, which does
absolutely nothing except including the header file "MyClass.hpp"
#include <MyClass.hpp>
int main(int argc, char **argv)
{
return 0;
}
Now, let's consider that the content of "MyClass.hpp" looks as follows:
#include <vector>
class Data1;
class MyClass
{
public:
MyClass(); // default constructor
MyClass(MyClass const & p_other); // copy constructor
MyClass(MyClass && p_other); // move constructor
~MyClass(); // destructor
MyClass & operator=(MyClass const & p_rhs); // copy assignment
MyClass & operator=(MyClass && p_rhs); // move assignment
private:
Data1 * m_a; // forward decl. always OK
Data1 m_b; // forward decl. always not OK
std::vector<Data1> m_c; // forward decl. OK if MyClass is not instantiated
};
MyClass contains several member variables:
"m_a" is a pointer to "Data1",
"m_b" is an instance of "Data1",
"m_c" is a vector with elements of type "Data1".
The definition of "Data1" is neither included in "MyClass.hpp" nor in "main.cpp". It is just forward declared.
The compiler never has a problem with member variable "m_a", it doesn't require its definition for compiling "main.cpp" -- even if we would intantiate "MyClass".
Because we are not instantiating "MyClass", the compiler also doesn't have a problem with member variable "m_c". It also does not require the definition of "Data1" in this case.
However, the compiler has a problem with member variable "m_b":
Impl1.hpp:16:24: error: field 'm_b' has incomplete type 'Data1'
My simple question is: why?
Please consider that we have user-defined default constructor, copy constructor,
move constructor, a user-defined destructor, and user-defined copy assigment
operator and move assignment operator, i.e., the compiler doesn't have to
generate code for any of these constructors/methods. So for what purpose does
the compiler need to see the definition of "Data1" when compiling only "main.cpp"?
Does anybody know the answer?
The size of MyClass depends on the size of Data1, which can only be known from the definition of Data1. That's why you cannot have incomplete types as fields in your types.

Why does this operator overloading work? [duplicate]

This question already has answers here:
I defined a non-copy constructor; will a copy constructor still be implicitly defined?
(3 answers)
Closed 6 years ago.
I am trying to learn C++ OOP concepts through an online tutorial where I encountered a code snippet illustrating operator overloading.
The code is shown below:
class MyClass{
int var;
public:
Myclass(int value=0){
var = value;
}
Myclass operator+(Myclass &obj){
Myclass newobj;
newobj.var = this->var + obj.var;
return newobj;
}
};
Suppose I call the operator in the main function like so:
int main(){
...
obj3 = obj2 + obj1;
...
}
During earlier tutorials on Classes, I read about why copy constructors require all parameters to be passed by reference since they themselves are the definition of how to copy two class objects. So, as far as I understand, copy constructors are a must when one has to copy objects of a class.
In the above code snippet, it appears to me that the compiler will try to "copy" the values of newobj onto the L_value in the main() function (obj3). But how is this possible without a copy constructor defined. Have I misunderstood something here?
Thank you for your help!
http://en.cppreference.com/w/cpp/language/copy_constructor#Implicitly-declared_copy_constructor
If you are using standard C++ 2003 or older copy constructor is always implicitly defined (generated by compiler) for any class T unless:
T has non-static data members that cannot be copied (have deleted, inaccessible, or ambiguous copy constructors);
T has direct or virtual base class that cannot be copied (has deleted, inaccessible, or ambiguous copy constructors);
T has direct or virtual base class with a deleted or inaccessible destructor;
If you are using standard C++ 2011 or newer copy constructor is always implicitly defined (generated by compiler) for any class T unless:
T has non-static data members that cannot be copied (have deleted, inaccessible, or ambiguous copy constructors);
T has direct or virtual base class that cannot be copied (has deleted, inaccessible, or ambiguous copy constructors);
T has direct or virtual base class with a deleted or inaccessible destructor;
T has a user-defined move constructor or move assignment operator;
T is a union and has a variant member with non-trivial copy constructor;
T has a data member of rvalue reference type.
Also keep in mind that
a = b;
is not primarily calling copy-constructor but copy-assignment. That in turn is also implicitly defined (auto-generated) if your class if suitable.
For details see: http://en.cppreference.com/w/cpp/language/copy_assignment#Implicitly-declared_copy_assignment_operator

Possible bug in unique_ptr implementation [duplicate]

This question already has answers here:
Unique_ptr and forward declaration
(2 answers)
Closed 6 years ago.
I was trying to use unique_ptr class member with forward declaration. As numerous sources says e.g. Forward declaration with unique_ptr? it should be sufficient to declare non inline destructor, but it seems not to be a case in VS2013 and GCC 5.3.1. I didn't test other compilers.
Example:
#include <memory>
class B;
class A {
public:
//A();
~A();
private:
std::unique_ptr<B> b;
};
//class B { };
int main() {
A a;
}
I can make this code compile only after uncommenting the ctor declaration or the class B declaration. Otherwise on VS2013 I get error
error C2338: can't delete an incomplete type
on GCC error:
In file included from /usr/local/include/c++/5.3.0/memory:81:0,
from main.cpp:1:
/usr/local/include/c++/5.3.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = B]':
/usr/local/include/c++/5.3.0/bits/unique_ptr.h:236:17: required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = B; _Dp = std::default_delete<B>]'
main.cpp:5:7: required from here
/usr/local/include/c++/5.3.0/bits/unique_ptr.h:74:22: error: invalid application of 'sizeof' to incomplete type 'B'
static_assert(sizeof(_Tp)>0,
^
Why is this?
The destructor of class A must know the definition of class B. A forward declaration of class B is okay as longs as the implementation file of the constructor/destructor of A knows the definition of class B. If your implementation is (implicitly) in a header file then you need a definition of B in your header file. You may study Pimpl from Herb Sutter.

Forward declaration with unique_ptr? [duplicate]

This question already has answers here:
Is std::unique_ptr<T> required to know the full definition of T?
(9 answers)
Closed 8 years ago.
I have found it useful to use forward declaration of classes in combination with std::unique_ptr as in the code below. It compiles and works with GCC, but the whole thing seem kind of strange, and I wonder if this is standard behaviour (i.e. required by the standard)? Since B isn't a complete type when I declare the unique_ptr.
A.hpp
#include <memory>
class B;
class A {
std::unique_ptr<B> myptr;
// B::~B() can't be seen from here
public:
~A();
};
A.cpp
#include "B.hpp"
//B.hpp has to be included, otherwise it doesn't work.
A::~A() = default; // without this line, it won't compile
// however, any destructor definiton will do.
I suspect this has to do with the destructor (and therefore the need to call the destructor of unique_ptr<B>) is defined in a specific compilation unit (A.cpp).
It's explicitly legal. The rule is that the types used to instantiate
a template in the standard library must be complete, unless otherwise
specified. In the case of unique_ptr, §20.7.1/5 says “[...] The
template parameter T of unique_ptr may be an incomplete type.”
There are certain operations on the pointer which require a complete
type; in particular, when the object will actually be destructed (at
least with the default deleter). In your example, for example, if
A::~A() were inline, this might cause problems. (Note that if you
don't declare the destructor yourself, it will be inline. Which
partially defeats the purpose of using std::unique_ptr.)