当模板类不是专用的时,成员函数的专用模板的解决方法

时间:2011-04-26 12:28:54

标签: c++ templates specialization

我正在使用类似std的接口构建数据结构类,并为数据结构实现不同的迭代器。

从概念上讲,我想做的是这样的事情:

template <class DataT>
class DataStructure
{
protected:
    DataT& Data;
public:
    DataStructure(DataT& data) : Data(data) {}
    class BaseIterator
    {
    public:
        BaseIterator()
        {
            cout<<"BaseIterator"<<endl;
        }
    };

    class DerrivedIterator1 : public BaseIterator
    {
    public:
        DerrivedIterator1()
        {
            cout<<"DerrivedIterator1"<<endl;
        }
    };

    class DerrivedIterator2 : public BaseIterator
    {
    public:
        DerrivedIterator2()
        {
            cout<<"DerrivedIterator2"<<endl;
        }
    };

    template<class IterT>
    IterT Begin()
    {
        //none-specialized implementation. Possibly throw exception
    }

    template<>
    DerrivedIterator1 Begin<DerrivedIterator1>()
    {
        //Find beginning for DerrivedIterator1
    }

    template<>
    DerrivedIterator2 Begin<DerrivedIterator2>()
    {
        //Find beginning for DerrivedIterator1
    }
};

但是这当然不能编译,因为C ++不允许在非专用模板容器中专门化模板成员函数。

显而易见的解决方法当然是声明2个不同的函数:Begin_Iterator1和Begin_Iterator2并完成它。但我正在寻找一种不会改变界面的解决方法。

有什么想法吗?

编辑:我忘了提到这是用于硬件分配的,所以提升甚至标准都不是一种选择。

2 个答案:

答案 0 :(得分:1)

函数模板不能专门用于C ++,指向。

它们是否是模板的成员并不重要,不允许使用功能模板。通常,当使用参数类型来推断模板参数时,重载会执行相同的特化,因此不需要对函数进行特化(以及重载决策中相关的额外复杂性等)。

但是,您没有任何参数可以推断并手动实例化模板。否,

DataStructure::DerivedIterator1 i = dataStructure.Begin();

在编写代码时工作,因为类型推断就像重载解析一样只对参数进行,而不是预期的返回值。你必须写:

DataStructure::DerivedIterator1 i = dataStructure.Begin<DataStructure::DerivedIterator1>();

并且没有任何好处:

DataStructure::DerivedIterator1 i = dataStructure.BeginIterator1();

但是,可以使用第一个表达式来处理某些魔法。首先,你必须定义BeginIterator1BeginIterator2而不是你做一个临时的,以决定构建哪一个:

class DataStructure {
    ...
    class BeginIteratorConstructor {
        DataStructure &dataStructure;
    public:
        BeginIteratorConstructor(DataStructure &ds) : dataStructure(ds) {}
        operator DerivedIterator1() { return dataStructure.BeginIterator1(); }
        operator DerivedIterator2() { return dataStructure.BeginIterator2(); }
    };
    BeginIteratorConstructor Begin() { return BeginIteratorConstructor(*this); }
    ...
};

现在dataStructure.Begin()将返回一个临时内容,如果您将其投放到BeginIterator1,则会调用DerivedIterator1,或者当您将其转换为BeginIterator2时调用DerivedIterator2 。如果你将它传递给编译器无法决定转换为哪一个,它会因为模糊的重载而死,或者因为BeginIteratorConstructor实际上不是迭代器,你必须明确地转换它。 / p>

(你应该谨慎地制作BeginIteratorConstructor私有,但我不确定编译器允许你走多远,所以你需要进行一些实验)

答案 1 :(得分:0)

您可以使用标记系统,它将使您免于类模板中的部分专用功能:

struct base_iter_tag{};
struct der1_iter_tag{};
struct der2_iter_tag{};

template<class T>
struct iter_type;

template<>
struct iter_type<BaseIterator>{
  typedef base_iter_tag tag;
};

template<>
struct iter_type<DerivedIterator1>{
  typedef der1_iter_tag tag;
};

template<>
struct iter_type<DerivedIterator2>{
  typedef der2_iter_tag tag;
};

template<class IterT>
IterT Begin(){
  return DoBegin(typename iter_type<IterT>::tag());
}

BaseIterator DoBegin(base_iter_tag){
  // ...
}

DerivedIterator1 DoBegin(der1_iter_tag){
  // ...
}

DerivedIterator2 DoBegin(der2_iter_tag){
  // ...
}

这基本上是标准库对iterator_traits<T>::iterator_category和重载函数的处理方式,具体取决于类别(例如forward_iterator_tagrandom_access_iterator_tag等等。)