遵循的原则:当我想从一个函数返回一个集合时,我将传递一个输出迭代器,并让调用者决定输出应该去哪里。
请考虑具有n
方法的类,每个方法都返回一些集合。这意味着我需要使用n
模板参数(输出迭代器)构造类。模板参数的数量将开始增加,我不知道该如何处理。
具体示例:
template<class TNode, class TEdge> class AGraph;
template<class TNode, class TEdge, class OutputOfFunc1, class OutputOfFunc2>
class APathCalculation
{
using TGraph = AGraph<TNode, TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, OutputOfFunc1 outPath) = 0;//func1
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, OutputOfFunc2 outDistances) = 0;//func2
};
而且,我将从APathCalculation
派生出不同的类(例如Dijkstra,Bellman-Ford)。但是问题是我引入了模板参数
...class OutputOfFunc1, class OutputOfFunc2>
我跌倒了,因为它们特定于特定的功能,所以它们不应在类定义中。
目前,我声明这样的类
// Example of declaration
APathCalculation<
int, // type of node
double, // type of edge
back_insert_iterator<list<size_t>>, // return type of shortest path between two nodes
back_insert_iterator<vector<double>> // return type of shortest distances from source node
> &pathCalculator;
答案 0 :(得分:2)
“考虑具有n个方法的类,每个方法都返回一些 采集。这意味着我需要使用n模板构造类 参数(输出迭代器)。
不,你不知道。您实际上可以创建具有0个模板参数的类。但是,每种方法本身都有一个模板参数。就您而言,您可以将其减少为该类的2个模板参数:
template<class TNode, class TEdge>
class APathCalculation
{
using TGraph = AGraph<TNode, TEdge>;
public:
template<class OutputOfFunc1>
void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, OutputOfFunc1 outPath);
template<class OutputOfFunc2>
void ReturnAllShortestDistances(size_t source, TGraph& graph, OutputOfFunc2 outDistances);
};
请注意,我在这里做了一个重要的更改:这是面向对象的类。您所拥有的是一个抽象类或接口。抽象类是使调用方和被调用方解耦的一种好方法,但是在这里不能对其进行解耦:调用方和被调用方必须在迭代器类型上达成共识。
答案 1 :(得分:1)
这里有三种可供选择的方案:
选项1:只需选择一种类型并返回
如果您担心性能,这可能不如您想像的那么糟糕。
virtual std::vector<TEdge> FindShortestPath(size_t source, size_t dest, TGraph& graph) = 0;
选项2:接受回叫
这个想法是,调用者将提供一个lambda来以任何有意义的方式存储输出。
virtual void TraverseShortestPath(
size_t source,
size_t dest,
TGraph& graph,
std::function<void(TEdge*)> callback) = 0;
选项3:使用功能模板
我为什么要为此使用多态性,这有点神秘。您可以为不同的最短路径算法(类似于STL中的算法样式)编写函数模板:
template <class TGraph, class OutIt>
void FindShortestPath(size_t source, size_t dest, TGraph& graph, OutIt output)
{
// details...
}
您当然可以对这些方法进行很多更改。我还要警告不要使用这样的输出迭代器。调用者可以传递诸如back_inserter
之类的安全信息,但也可以传递诸如原始指针之类的危险信息,这很容易导致缓冲区溢出。
答案 2 :(得分:0)
您可以在单个结构中定义所有参数,并在整个结构中使用它们,如下所示:
template<class TNode, class TEdge> class AGraph;
template< typename TYPE_DEFS >
class APathCalculation
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) = 0;//func1
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) = 0;//func2
};
template< typename TYPE_DEFS >
class Dijkstra: public APathCalculation<TYPE_DEFS>
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) override {}
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) override {}
};
struct TypeDefs
{
using TNode = int;
using TEdge = double;
using OutputOfFunc1 = std::back_insert_iterator<std::list<size_t>>;
using OutputOfFunc2 = std::back_insert_iterator<std::vector<double>>;
};
int main()
{
Dijkstra<TypeDefs> d;
}
所有这些都不会改变您的二进制文件。这只会使其更加方便。但是也许我误解了你的问题?
答案 3 :(得分:0)
“浅”答案:
为了避免在一类中使用N个模板参数,您需要将其分为N个类,每个类具有1个模板参数。
“深层”答案:
您无法轻松地将虚拟函数的动态(运行时)多态与参数类型的静态(基于模板的编译时)多态结合起来。如果APathCalculation
接口的用户想要提供自己的迭代器类,则需要为其迭代器类实例化该接口的所有可能有用的实际实现,这使得APathCalculation
的动态多态性成为多余的想法
如果您确实需要动态多态性,则需要一个动态多态迭代器类。
您实际上可以同时拥有(用于通用算法和/或通用迭代器的静态多态模板专门化,并以“默认”情况作为后盾,该情况是动态多态实现的包装),但这可能对您的任务而言太复杂了。此外,您仍然需要一种方法将您的“默认”包装器绑定到您希望APathCalculation
客户端调用的实际算法上。
我个人将首先从基于模板的解决方案开始,但是我可以理解从纯基于虚拟函数的解决方案开始的人。