我正在使用类似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并完成它。但我正在寻找一种不会改变界面的解决方法。
有什么想法吗?
编辑:我忘了提到这是用于硬件分配的,所以提升甚至标准都不是一种选择。
答案 0 :(得分:1)
函数模板不能专门用于C ++,指向。
它们是否是模板的成员并不重要,不允许使用功能模板。通常,当使用参数类型来推断模板参数时,重载会执行相同的特化,因此不需要对函数进行特化(以及重载决策中相关的额外复杂性等)。
但是,您没有任何参数可以推断并手动实例化模板。否,
DataStructure::DerivedIterator1 i = dataStructure.Begin();
不在编写代码时工作,因为类型推断就像重载解析一样只对参数进行,而不是预期的返回值。你必须写:
DataStructure::DerivedIterator1 i = dataStructure.Begin<DataStructure::DerivedIterator1>();
并且没有任何好处:
DataStructure::DerivedIterator1 i = dataStructure.BeginIterator1();
但是,可以使用第一个表达式来处理某些魔法。首先,你必须定义BeginIterator1
和BeginIterator2
而不是你做一个临时的,以决定构建哪一个:
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_tag
,random_access_iterator_tag
等等。)