1

Let's start with the warm-up.

I created a class template whose destructor just cannot be instantiated without compiler activating static_assert:

File: M.hpp

template <typename T>
struct M
{
    M(){}
    ~M()
    {
        static_assert(sizeof(T) == 0, "no type can pass this");
    }
};

Next I used this class in two different ways: allocate it on heap and allocate it on stack:

File main.cpp

#include "M.hpp"

int main()
{
    M<int> *pmi = new M<int>(); // does compile
    M<int> mi;                  // doen't compile
}

Allocating it on a heap M<int> *pmi = new M<int>(); works. It is so as we only use constructor and don't need destructor. The rules for class template implicit instantiation say:

... unless the member is used in the program, it is not instantiated ...

Allocating it on stack M<int> mi; doesn't work as compiler definitely need to instantiate the destructor.

So far so good, clear rules.

Let's get to the point.

I wrote another class which uses M as a member:

File X.cpp

#include "M.hpp"

struct X
{
    X() = default;
    ~X() = delete;

private:
    M<int> m_;
};

I deleted the destructor intentionally because I don't want it to interfere anyhow with my experiment. Constructor is defaulted which in my understanding should only generate constructor of M<int>, its only member, and call it. To my surprise this is not the case. X() also tries to generate the destructor of M<int>:

File main.cpp

#include "X.hpp"

int main()
{
  X* px = new X();
}

and this is what I get from a compiler:

$ g++ -std=c++17 main.cpp
In file included from X.hpp:1,
                 from main.cpp:1:
M.hpp: In instantiation of ‘M<T>::~M() [with T = int]’:
X.hpp:5:3:   required from here
M.hpp:7:29: error: static assertion failed: no type can pass this
     static_assert(sizeof(T) == 0, "no type can pass this");

The question is: why during the instantiation of the default constructor a compiler tries to instantiate a destructor for a member class template if it is not needed? If it is indeed needed could you point me at the documentation where it is stated?

user2551229
  • 355
  • 5
  • 13

1 Answers1

3

The constructor of your X potentially invokes destructors of its member subobjects for obvious reason: if an exception occurs during construction, everything that has been successfully constructed has to be destructed.

10.9.2 Initializing bases and members

12 In a non-delegating constructor, the destructor for each potentially constructed subobject of class type is potentially invoked (15.4). [ Note: This provision ensures that destructors can be called for fully-constructed subobjects in case an exception is thrown (18.2). —end note ]

http://eel.is/c++draft/class.base.init#12

Community
  • 1
  • 1
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    I thought (maybe I'm mistaken) that default implementations of ctors are `noexcept` if possible, which should be the case for `M` and thus also for `X` here. So why is the dtor still potentially invoked, if no exception can be thrown? – sebrockm Oct 10 '18 at 23:21