定义一个函数,使其能够接受列表或向量。

4
我有一个函数,需要接收MyClass *对象的std::liststd::vector,并根据里面的内容进行一系列处理。 我不想重复函数体。
我只需要用这些对象来迭代并进行一些只读检查。
我曾考虑过直接传递.begin().end()迭代器,但那看起来不太美观。
有没有更好的方法解决这个问题,目前我有以下解决方案(从传递的向量中创建另一个列表,这也几乎是不理想的)。
void process(std::list<MyClass*> input)
{
    //A lot of processing
    BOOST_FOREACH(MyClass* itMyClass, input)
    {
        //some checks, creating new list based on the checks      
    }
    //A lot of processing
}
void process(std::vector<MyClass*> input)
{
    process(std::list<MyClass*>(input.begin(), input.end()));
}

编辑:

看来很多人建议最终选择使用begin()end(),我已经让它以类似下面示例的方式工作。感谢您的帮助。

//This one is private
template <typename Iterator>
void process(Iterator begin, Iterator end)
{
    //A lot of processing
    for (; begin != end; ++begin) 
    {
        //some checks, creating new list based on the checks
    }
    //A lot of processing
}
void process(std::list<MyClass*> input)
{
    process(input.begin(), input.end());
}
void process(std::vector<MyClass*> input)
{
    process(input.begin(), input.end());
}

3
使用模板? - miradulo
https://dev59.com/MWsz5IYBdhLWcg3wrJ-- - Rietty
1
尽管迭代器看起来不太美观,但它们与标准库一致且非常灵活。重新考虑一下你对它们的立场是值得的。 - patatahooligan
1
我也赞成直接传递beginend迭代器。这是将算法与要处理的容器解耦的最佳方式。即使它看起来不太美观,但STL是一项天才的工作。 - Galik
1
或者,您可以传递一个起始和结束迭代器,而不是容器。(如果您正在迭代,则有效,但如果您正在改变容器,则效果不佳。) - Eljay
1
看起来begin()和end()是人们的选择,这也是我的第一想法。我相信我会有两个公共的重载函数,一个是针对vector,另一个是针对list。两者都将传递begin()和end()迭代器给私有函数,由此实现逻辑功能。谢谢。 - Yu Ko
2个回答

4
您可以使用函数模板来实现这个功能:

函数模板

template<class ListOrVector>
void process(ListOrVector const& input) {
    //your code
}

//You can also use a template template parameter
template<template<class My, class Alloc = std::allocator<My>> class ListOrVector>
void process(ListOrVector<MyClass*, Alloc> const& input) { ... }

请注意,我是通过const引用(const &)来获取ListOrVector的。这将防止复制。 编辑 我已经修正了第二个示例。ListOrVector之前缺少class,默认情况下分配器为std::allocator<My

@YuKo 您可能完全不关心分配器,因此您可以将其简化为:template<template<class My> class ListOrVector> void process(ListOrVector<MyClass*> const& input)。 - Kilian

0

就像普通函数一样,函数模板也可以进行重载,这样您就可以拥有最好的两个世界:更灵活的基于迭代器的算法和更易用的基于容器的算法。

通过这种方式,您可以使用基于迭代器的重载来处理容器的子范围,并使用基于容器的重载来处理容器的所有元素。


process(first, last)

我建议首先定义一个函数模板process(),它接受一个迭代器对,用于处理您想要处理的元素序列:

template<typename Iterator>
void process(Iterator begin, Iterator end) {
   for (auto it = begin; it != end; ++it) {
      // ...
   }
}

这是一种基于迭代器的算法(即,它接受一个迭代器对),并且对应于STL算法遵循的相同方法。


process(container)

然后,我会定义另一个函数模板 process(),它重载了第一个函数。这个重载版本接受一个容器并在传递的元素上调用基于迭代器的 process() 版本:

template<typename Container>
void process(Container const& c) {
   process(std::begin(c), std::end(c));
}

这是一个基于容器的算法,因为它需要一个容器作为输入。

这样,您可以使用基于容器的算法而不是基于迭代器的算法:

std::vector<int> vec{1, 2, 3};
std::list<int> lst{1, 2, 3};
std::array<int, 3> arr{1, 2, 3};
int carr[] = {1, 2, 3};

process(vec);
process(lst);
process(arr);
process(carr);

这个算法与处理容器解耦(例如,它甚至可以使用C风格的数组,正如您所看到的),但您直接在容器上调用它,而不是传递迭代器。

当您不想处理容器中的所有元素,而只想处理其中的子范围时,您始终可以调用process()的重载,该重载接受一个迭代器对而不是一个容器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接