0

我正在构建一个具有类标准接口的数据结构类,并为数据结构实现不同的迭代器。

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

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 并完成它。但我正在寻找一种不会改变界面的解决方法。

有任何想法吗?

编辑:我忘了提到这是针对硬件分配的,因此 boost 甚至 std 都不是一个选项。

4

2 回答 2

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实际上不是迭代器而死,你必须显式地转换它。

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

于 2011-04-26T13:10:33.193 回答
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_tag所做random_access_iterator_tag的事情。

于 2011-04-26T13:14:02.180 回答