Avraam Mavridis
Avraam Mavridis

Reputation: 8920

Protected destructor of policy Classes in C++

I wrote the following dumb policy struct:

template
<typename T>
struct SuperLeague
{
public:
  void printLeague(){std::cout << "SuperLegue" << std::endl;};
protected:
  //~SuperLeague(){}
};

and the host class

template
<typename TT,template <typename C> class Policy>
class League: public Policy<TT>
{
public:
  void fun()
  {
    Policy<TT>().printLeague();
  }
};

My main is

int main()
{
  League<int,SuperLeague> k;
  League<int,SuperLeague> kk;
  k.fun();
  kk.printLeague();
  League<int,SuperLeague> *kkk=new League<int,SuperLeague>();
  kkk->fun();
  //delete kkk;
};

Until here, everything works fine. And the output is:

SuperLegue
SuperLegue
SuperLegue

In one of his Books Andrei Alexandrescu writes: Unless the policy class defines a virtual destructor, applying delete to a pointer to the policy class has undefined behavior. He explains the reasons not to use virtual destructor on the policy or protected (or private) inheritance when deriving from the policy class, and he suggests The lightweight, effective solution that policies should use is to define a nonvirtual protected destructor. The problem is that when I tried to do that using ~SuperLeague(){} the compiler complains that the destructor is protected. What I am doing wrong?

Upvotes: 0

Views: 576

Answers (2)

Andy Prowl
Andy Prowl

Reputation: 126522

You shouldn't be creating a temporary policy object inside League::fun(). Since your instance of the League template derives from the appropriate instance of the Policy template, it inherits the printLeague() function:

template
<typename TT,template <typename C> class Policy>
class League: public Policy<TT>
{
public:
  void fun()
  {
    Policy<TT>::printLeague();
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^
  }
};

The reason why your solution won't compile when you declare the destructor protected is that protected makes base class members accessible from a derived class when accessing an object (or dereferencing a reference or pointer to an object) of the same derived class (League in your case).

That is not the case in your example, where you create a temporary object of type Policy<TT> and invoke printLeague() on that object (which is not of type League).

Per paragraph 11.4/1 of the C++11 Standard:

An additional access check beyond those described earlier in Clause 11 is applied when a non-static data member or non-static member function is a protected member of its naming class (11.2). As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member (5.3.1), the nested-name-specifier shall denote C or a class derived from C. All other accesses involve a (possibly implicit) object expression (5.2.5). In this case, the class of the object expression shall be C or a class derived from C.

Upvotes: 2

Pete Becker
Pete Becker

Reputation: 76448

Policy<TT>().printLeague();

This creates an object of type Policy<TT>, calls printLeague() on that object, then destroys the object. The compiler is complaining about destroying the object, because the destructor is protected.

Since Policy<TT> is a base class, just call printLeague() directly.

Upvotes: 1

Related Questions