    Some software engineers have reservations when thinking about the
tt(Storage) class that uses pointers to store copies of value class
objects. Their argument is that value class objects can very well be stored by
value, rather than by pointer. They'd rather store value class objects by
value and polymorphic class objects by pointer.

    Such distinctions  frequently occur in template meta programming and
the following tt(struct IfElse)
        hi(template: IfElse)
        hi(template: select type given bool)
    may be used to obtain one of two types, depending on a tt(bool) selector
value.

    First define the em(generic form) of the template:
        verb(
    template<bool selector, typename FirstType, typename SecondType>
    struct IfElse
    {
        typedef FirstType type;
    };
        )
    Then define a partial specialization. The specialization represents
a specific selector value (e.g., tt(false)) and leaves the remaining types
open to further specification:
        verb(
    template<typename FirstType, typename SecondType>
    struct IfElse<false, FirstType, SecondType>
    {
        typedef SecondType type;
    };
        )
    The former (generic) definition associates tt(FirstType) with the
tt(IfElse::type) type definition, the latter definition (partially specialized
for the logical value tt(false)) associates tt(SecondType) with the
tt(IfElse::type) type definition.

The tt(IfElse) template allows us to define class templates whose data
organization is conditional to the template's parameters.
    hi(class template: conditional data organization)
 Using tt(IfElse) the tt(Storage) class may define
em(pointers) to store copies of polymorphic class type objects and em(values)
to store value class type objects:
        verb(
    template <typename Type, bool isPolymorphic>
    class Storage
    {
        typedef typename IfElse<isPolymorphic, Type *, Type>::type
                DataType;

        std::vector<DataType> d_data;

        private:
            void add(Type const &obj, IntType<true>);
            void add(Type const &obj, IntType<false>);
        public:
            void add(Type const &obj);
    }

    template <typename Type, bool isPolymorphic>
    void Storage<Type, isPolymorphic>::add(Type const &obj, IntType<true>)
    {
        d_data.push_back(obj.clone());
    }

    template <typename Type, bool isPolymorphic>
    void Storage<Type, isPolymorphic>::add(Type const &obj, IntType<false>)
    {
        d_data.push_back(obj);
    }

    template <typename Type, bool isPolymorphic>
    void Storage<Type, isPolymorphic>::add(Type const &obj)
    {
        add(obj, IntType<isPolymorphic>());
    }
        )
    The above example uses tt(IfElse)'s tt(type), defined by tt(IfElse) as
either tt(FirstType) or tt(SecondType). tt(IfElse)'s tt(type) defines the
actual data type to use for tt(Storage)'s tt(vector) data type.

    The remarkable result in this example is that the em(data organization) of
the tt(Storage) class now depends on its template arguments. Since the
tt(isPolymorphic == true) situation uses different data types than the
tt(isPolymorphic == false) situation, the overloaded private tt(add) members
can utilize this difference immediately. E.g., tt(add(Type const &obj,
IntType<false>)) uses direct copy construction to store a copy of tt(obj)
in tt(d_vector).

    It is also possible to make a selection from multiple types as tt(IfElse)
structs can be nested. Realize that using tt(IfElse) never has any effect on
the size or execution time of the final executable program. The final program
simply contains the appropriate type, conditional to the type that's
eventually selected.
