Using
template-template-parameters with policy classes is not simply a matter
of convenience; sometimes, it is essential that the host class have
access to the template so that the host can instantiate it with a
different type. An alternative to using template-template-parameters is
to use template member functions in conjunction with simple classes.
That is, the policy implementation is a simple class (as opposed to a
template class) but has one or more templated members.
Modern
C++ Design Generic Programming and Design Patterns Applied – Page 6:
This analysis suggests that a combination of templates and multiple
inheritance could engender a very flexible device, appropriate for
creating libraries of design elements.
Modern
C++ Design Generic Programming and Design Patterns Applied – Page 9:
Note that policies are quite different from mere virtual functions.
Virtual functions promise a similar effect: The implementer of a class
defines higher-level functions in terms of primitive virtual functions
and lets the user override the behavior of those primitives. As shown
above, however, policies come with enriched type knowledge and static
binding, which are essential ingredients for building designs. Aren't
designs full of rules that dictate before runtime how types interact
with each other and what you can and what you cannot do? Policies allow
you to generate designs by combining simple choices in a type-safe
manner. In addition, because the binding between a host class and its
policies is done at compile time, the code is tight and efficient,
comparable to its handcrafted equivalent. Of course, policies' features
also make them unsuitable for dynamic binding and binary interfaces, so
in essence policies and classic interfaces do not compete.
Clients
who need enriched policies can benefit from that rich functionality,
without affecting the basic functionality of the host class. Don't
forget that users—and not the library—decide which policy class to use.
Unlike regular multiple interfaces, policies give the user the ability
to add functionality to a host class, in a type-safe manner.
There
is an additional important detail about creating policy classes. Most
often, the host class uses public inheritance to derive from its
policies. For this reason, the user can automatically convert a host
class to a policy and later delete that pointer. Unless the policy class
defines a virtual destructor, applying delete to a pointer to the
policy class has undefined behavior.
Defining
a virtual destructor for a policy, however, works against its static
nature and hurts performance. Many policies don't have any data members,
but rather are purely behavioral by nature. The first virtual function
added incurs some size overhead for the objects of that class, so the
virtual destructor should be avoided. A solution is to have the host
class use protected or private inheritance when deriving from the policy
class. However, this would disable enriched policies as well. The
lightweight, effective solution that policies should use is to define a
nonvirtual protected destructor:
template <class T>
struct OpNewCreator
{
static T* Create()
{
return new T;
}
protected:
~OpNewCreator() {}
};
Because
the destructor is protected, only derived classes can destroy policy
objects, so it's impossible for outsiders to apply delete to a pointer
to a policy class. The destructor, however, is not virtual, so there is
no size or speed overhead.
C++
contributes to the power of policies by providing an interesting
feature. If a member function of a class template is never used, it is
not even instantiated—the compiler does not look at it at all, except
perhaps for syntax checking.
Notes:
***.
No comments:
Post a Comment