C++ class template specialization with pointers

2019-08-19 20:27发布

问题:

I have a tree structure of the following format:

template <typename DataType>
class Tree {

    DataType *accessData() { return data; } 

    Tree *child1, *child2;
    DataType *data;
};

template <typename DataType>
class Root : public Tree<DataType> {
    // root provides storage of nodes; when it goes out of scope, the
    // entire tree becomes invalid
    MemoryPool<Tree> nodeStorage;
    MemoryPool<DataType> dataStorage; 
};

I use a variety of instantiations of this template in my program. It works quite well.

One instantiation, however, uses a DataType which is just an enum (so it's the same size as a pointer!) and because speed is essential (both when the tree is built, and when it is accessed), I'd much rather have this instantiation use the enum directly instead of a pointer. An example how I'd like the code to look (not strict):

Tree<BigClass> *foo = ...;
foo->accessData()->doBigClassThings();
Tree<int> *bar = ...;
int x = 4 + bar->accessInt();

Now of course I could just keep the current templates but I don't like this extra pointer access and especially the need to allocate ints in the root. Any ideas on how I can specialize the template to provide this functionality, or other approaches?

I've tried to specialize the template like this (and a bazillion other ways)

template <> Tree<int> { ... }

But I just keep getting compile errors. Any help would be greatly appreciated!

回答1:

I would recommend using a traits class to deduce the type of object stored in Tree.

// The default traits.
template <typename DataType> struct TreeDataType
{
   using Type = DataType*;
};

template <typename DataType>
class Tree {

   // Define the data type using the traits class.
   using Data = typename TreeDataType<DataType>::Type;

   Data accessData() { return data; } 

   Tree *child1, *child2;
   Data data;
};

and then specialize TreeDataType for MyEnum.

template <> struct TreeDataType<MyEnum>
{
   using Type = MyEnum;
};


回答2:

I suggest defining multiple data classes with the same interface that you can use as DataType template parameters. Abstract the way the data is stored from the way the data is accessed.

template<typename T>
class value_data
{
private:
    T _value;

public:
    T& access() { return _value; }
    const T& access() const { return _value; }
};

template<typename T>
class unique_ptr_data
{
private:
    std::unique_ptr<T> _value;

public:
    T& access() { assert(_value != nullptr); return *_value; }
    const T& access() const { assert(_value != nullptr); return *_value; }
};

enum class my_enum { /* ... */ };

class my_enum_data
{
private:
    my_enum _value;

public:
    my_enum& access() { return _value; }
    const my_enum& access() const { return _value; }
};

Then, in your Tree class, you can use them through their common interface:

template <typename DataType>
class Tree {

    auto& accessData() { return data.access(); } 

    Tree *child1, *child2;
    DataType data;
};