当模板类未特化时,成员函数的特化模板的解决方案

Workaround for specialized template for member function, when template class is not specialized

本文关键字:函数 解决方案 成员      更新时间:2023-10-16

我正在构建一个具有类似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++不允许在非特化的模板容器中特化模板成员函数。

明显的解决方法当然是声明两个不同的函数:Begin_Iterator1和Begin_Iterator2,然后完成它。但是我正在寻找一个不改变界面的解决方案。

任何想法?

编辑:我忘了说这是一个HW赋值,所以boost甚至std都不是一个选项。

函数模板不能在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()将返回一个临时的东西,如果你将其转换为DerivedIterator1,它将调用BeginIterator1,或者当你将其转换为DerivedIterator2时调用BeginIterator2。如果将其传递给某个对象,而编译器无法决定将其强制转换给哪一个对象,则它将死,要么是因为有二义性重载,要么是因为BeginIteratorConstructor实际上不是迭代器,因此必须显式强制转换。

(您应该小心地使尽可能多的BeginIteratorConstructor私有,但我不确定编译器将允许您走多远,所以您必须尝试一下)

您可以使用标记系统,它将使您免于在类模板中使用部分专门化的函数:

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){
  // ...
}

这基本上是标准库根据类别(例如forward_iterator_tag, random_access_iterator_tag等…)对iterator_traits<T>::iterator_category和重载函数所做的。

相关文章: