避免在模板参数中堆积迭代器类型

时间:2018-11-22 13:52:04

标签: c++ templates iterator

遵循的原则:当我想从一个函数返回一个集合时,我将传递一个输出迭代器,并让调用者决定输出应该去哪里。

请考虑具有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;  

4 个答案:

答案 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客户端调用的实际算法上。

我个人将首先从基于模板的解决方案开始,但是我可以理解从纯基于虚拟函数的解决方案开始的人。