I have a class with a vector
member variable whose T
is declared, but not defined. This can be a problem if no destructor for the class is defined, since the compiler might pick some other translation unit to generate the destructor in. If that TU doesn't have a definition for T
, compilation fails.
struct undefined;
struct S
{
S(int);
std::vector<undefined> v;
};
int main()
{
S s(42); // fails in ~vector(), `undefined` is undefined
}
To work around this problem, I usually add a defaulted destructor as an anchor in the file that implements the class, which has access to the definition.
struct undefined;
struct S
{
S(int);
~S(); // implemented in another TU as `S::~S() = default;`
std::vector<undefined> v;
};
int main()
{
S s(42); // ok
}
I'm trying to do the same thing with a derived class that inherits the base class' constructor:
#include <vector>
struct undefined;
struct base
{
base(int);
};
struct derived : base
{
using base::base;
~derived();
std::vector<undefined> v;
};
int main()
{
derived d(1); // fails in ~vector(), `undefined` is undefined
}
If I change derived
to not inherit the constructor, compilation succeeds:
#include <vector>
struct undefined;
struct base
{
base(int);
};
struct derived : base
{
//using base::base; <--- note
derived(int);
~derived();
std::vector<undefined> v;
};
int main()
{
derived d(1); // ok
}
What's really confusing to me is that clang says this:
stl_vector.h:336:35: error: arithmetic on a pointer to an incomplete type 'undefined'
[blah]
a.cpp:12:14: note: in instantiation of member function 'std::vector<undefined>::~vector' requested here
using base::base;
^
It sounds like using base::base
is also bringing in something that's destroying the vector
, but I have no idea what. I've tried deleting copy/move constructors/operators in both classes, but the error remains. Both g++ and Visual C++ fail with similar errors.
What's going on?
As mentioned by Raymond Chen in this comment, the reason
using base::base
requires thevector
's destructor is because exceptions may be thrown from the constructor, which may require destroying member variables. The only solution is to write the constructor manually.A constructor needs to destroy fully constructed member variables if an exception is thrown. In this example,
a
must be destroyed, but notb
:In the next example, the compiler generates a default constructor and a destructor. Both can call
~vector()
and so both need the definition ofincomplete
:Adding a declaration for both fixes the error:
But an inherited constructor is also generated by the compiler and therefore needs
~vector()
in case an exception is thrown:And so the only solution is to declare the constructor manually and implement it somewhere else.