C++ 通用设计混合了奇怪的重复模板模式。C++;
考虑这类问题。我有一个C++ 通用设计混合了奇怪的重复模板模式。C++;,c++,oop,templates,design-patterns,traits,C++,Oop,Templates,Design Patterns,Traits,考虑这类问题。我有一个Base类和三个派生自Base的类。例如:DerivedA、DerivedB和DerivedC。每个派生类都有其唯一的容器。因此DerivedA具有std::vector,DerivedB具有std::set和DerivedC具有std::map。我希望在Base中有一个接口来访问它当前指向的派生类的容器 Base* d1 = new DerivedA; for(std::vector<int>::iterator iter = d1->begin();
Base
类和三个派生自Base
的类。例如:DerivedA
、DerivedB
和DerivedC
。每个派生类都有其唯一的容器。因此DerivedA
具有std::vector
,DerivedB
具有std::set
和DerivedC
具有std::map
。我希望在Base
中有一个接口来访问它当前指向的派生类的容器
Base* d1 = new DerivedA;
for(std::vector<int>::iterator iter = d1->begin(); iter != d1->end(); ++iter)
{
//processing
}
给我提个建议。我有点迷失在这个可怕的设计中。为了做你想做的事情,你需要定义一个常见类型的迭代器,它可以从派生类中不同的
begin()
和end()
重写返回
当然,在此之前,您需要决定您希望迭代器做什么,正如雅克在评论中解释的那样。对于初学者来说,您需要决定通过这样一个迭代器间接执行将产生什么value\u type
。给定三个不同的容器,我能想到的唯一常见类型是const int
,因为std::map
s中的键是const
,std::set
迭代器是const
迭代器(因为元素本身就是键)。因此,当使用公共迭代器类型进行迭代时,您只能观察其中的int
s
现在,迭代器实现将需要调用不同的代码(在运行时),具体取决于它起源的派生类。这是类型擦除的典型用例。如果操作正确,这将允许您包装任何类型的迭代器,只要它支持您需要的接口。然而,在您的例子中,您可能不需要走那么远,因为我假设您知道需要支持的完整容器集,因此迭代器类型集也是众所周知的,并且也是有界的
这意味着您可以使用boost::variant
来存储包装的迭代器。这应该比完整类型擦除解决方案更有效,因为它避免了一些内部虚拟函数调用和可能的堆分配(除非类型擦除解决方案可以使用某种小对象优化,这对于迭代器来说是相当可能的,但实现起来更复杂)
下面是这样一个迭代器的框架实现,以及使用它的类层次结构和一些简单的测试代码。注意,我只实现了使循环工作所需的基本迭代器功能
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <iterator>
#include "boost/variant.hpp"
//Helper function object types to implement each operator on the variant iterator.
struct indirection_visitor : boost::static_visitor<const int&>
{
const int& operator()(std::vector<int>::iterator i) const { return *i; }
const int& operator()(std::set<int>::iterator i) const { return *i; }
const int& operator()(std::map<int, std::string>::iterator i) const { return i->first; }
};
struct prefix_increment_visitor : boost::static_visitor<>
{
template<typename I> void operator()(I& i) const { ++i; }
};
//The iterator itself.
//It should probably hide the internal variant, in which case the non-member operators
//should be declared as friends.
struct var_iterator : std::iterator<std::bidirectional_iterator_tag, const int>
{
var_iterator() { }
template<typename I> var_iterator(I i) : it(i) { }
boost::variant<std::vector<int>::iterator, std::set<int>::iterator, std::map<int, std::string>::iterator> it;
const int& operator*() { return boost::apply_visitor(indirection_visitor(), it); }
var_iterator& operator++()
{
boost::apply_visitor(prefix_increment_visitor(), it);
return *this;
}
};
inline bool operator==(var_iterator i1, var_iterator i2) { return i1.it == i2.it; }
inline bool operator!=(var_iterator i1, var_iterator i2) { return !(i1 == i2); }
//Here's the class hierarchy.
//We use CRTP only to avoid copying and pasting the begin() and end() overrides for each derived class.
struct Base
{
virtual var_iterator begin() = 0;
virtual var_iterator end() = 0;
};
template<typename D> struct Base_container : Base
{
var_iterator begin() override { return static_cast<D*>(this)->container.begin(); }
var_iterator end() override { return static_cast<D*>(this)->container.end(); }
};
struct DerivedA : Base_container<DerivedA>
{
std::vector<int> container;
};
struct DerivedB : Base_container<DerivedB>
{
std::set<int> container;
};
struct DerivedC : Base_container<DerivedC>
{
std::map<int, std::string> container;
};
//Quick test.
void f(Base* bp)
{
for(auto iter = bp->begin(); iter != bp->end(); ++iter)
{
std::cout << *iter << ' ';
}
std::cout << '\n';
//We have enough to make range-based for work too.
for(auto i : *bp)
std::cout << i << ' ';
std::cout << '\n';
}
int main()
{
DerivedA da;
da.container = {1, 2, 3};
f(&da);
DerivedB db;
db.container = {4, 5, 6};
f(&db);
DerivedC dc;
dc.container = std::map<int, std::string>{{7, "seven"}, {8, "eight"}, {9, "nine"}};
f(&dc);
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括“增压/变型.hpp”
//帮助函数对象类型,以实现变量迭代器上的每个运算符。
结构间接\u访问者:boost::static\u访问者
{
const int&operator()(std::vector::iterator i)const{return*i;}
const int&operator()(std::set::iterator i)const{return*i;}
const int&operator()(std::map::iterator i)const{return i->first;}
};
结构前缀\u增量\u访问者:boost::static\u访问者
{
模板void运算符()(I&I)常量{++I;}
};
//迭代器本身。
//它可能会隐藏内部变量,在这种情况下,非成员运算符
//应该被宣布为朋友。
结构变量迭代器:std::迭代器
{
var_迭代器(){}
模板变量迭代器(I):它(I){
促进:改变它;
常量int&运算符*(){return boost::apply_visitor(间接_visitor(),it);}
var_迭代器和运算符++()
{
boost::apply_visitor(前缀_increment_visitor(),it);
归还*这个;
}
};
内联布尔运算符==(var_迭代器i1,var_迭代器i2){return i1.it==i2.it;}
内联布尔运算符=(var_迭代器i1,var_迭代器i2){return!(i1==i2);}
//这是类的层次结构。
//我们使用CRTP只是为了避免复制和粘贴每个派生类的begin()和end()重写。
结构基
{
虚拟变量迭代器begin()=0;
虚拟变量迭代器end()=0;
};
模板结构基本容器:基本
{
var_迭代器begin()重写{return static_cast(this)->container.begin();}
var_迭代器end()重写{return static_cast(this)->container.end();}
};
struct DerivedA:Base\u容器
{
载体容器;
};
结构DerivedB:基本容器
{
std::set容器;
};
struct DerivedC:Base\u容器
{
映射容器;
};
//快速测试。
空f(基准*bp)
{
对于(自动iter=bp->begin();iter!=bp->end();++iter)
{
std::cout您需要什么访问容器的权限?开始迭代器和结束迭代器。这就足够了(我指的是迭代器)满足您的需要。但是您需要什么呢。在很少的情况下,您需要这些迭代器。您有一个问题。您想出了一个解决方案:但这个(对您来说很小)你不理解它的问题。你不理解它的事实意味着你不知道它有多大。备份:是什么问题促使你走这条路?你想迭代元素吗?迭代键吗?什么代码可以迭代int->string映射和一组int?
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <iterator>
#include "boost/variant.hpp"
//Helper function object types to implement each operator on the variant iterator.
struct indirection_visitor : boost::static_visitor<const int&>
{
const int& operator()(std::vector<int>::iterator i) const { return *i; }
const int& operator()(std::set<int>::iterator i) const { return *i; }
const int& operator()(std::map<int, std::string>::iterator i) const { return i->first; }
};
struct prefix_increment_visitor : boost::static_visitor<>
{
template<typename I> void operator()(I& i) const { ++i; }
};
//The iterator itself.
//It should probably hide the internal variant, in which case the non-member operators
//should be declared as friends.
struct var_iterator : std::iterator<std::bidirectional_iterator_tag, const int>
{
var_iterator() { }
template<typename I> var_iterator(I i) : it(i) { }
boost::variant<std::vector<int>::iterator, std::set<int>::iterator, std::map<int, std::string>::iterator> it;
const int& operator*() { return boost::apply_visitor(indirection_visitor(), it); }
var_iterator& operator++()
{
boost::apply_visitor(prefix_increment_visitor(), it);
return *this;
}
};
inline bool operator==(var_iterator i1, var_iterator i2) { return i1.it == i2.it; }
inline bool operator!=(var_iterator i1, var_iterator i2) { return !(i1 == i2); }
//Here's the class hierarchy.
//We use CRTP only to avoid copying and pasting the begin() and end() overrides for each derived class.
struct Base
{
virtual var_iterator begin() = 0;
virtual var_iterator end() = 0;
};
template<typename D> struct Base_container : Base
{
var_iterator begin() override { return static_cast<D*>(this)->container.begin(); }
var_iterator end() override { return static_cast<D*>(this)->container.end(); }
};
struct DerivedA : Base_container<DerivedA>
{
std::vector<int> container;
};
struct DerivedB : Base_container<DerivedB>
{
std::set<int> container;
};
struct DerivedC : Base_container<DerivedC>
{
std::map<int, std::string> container;
};
//Quick test.
void f(Base* bp)
{
for(auto iter = bp->begin(); iter != bp->end(); ++iter)
{
std::cout << *iter << ' ';
}
std::cout << '\n';
//We have enough to make range-based for work too.
for(auto i : *bp)
std::cout << i << ' ';
std::cout << '\n';
}
int main()
{
DerivedA da;
da.container = {1, 2, 3};
f(&da);
DerivedB db;
db.container = {4, 5, 6};
f(&db);
DerivedC dc;
dc.container = std::map<int, std::string>{{7, "seven"}, {8, "eight"}, {9, "nine"}};
f(&dc);
}