如何为接受STL容器迭代器的函数提供功能签名?

9
我想编写一个函数my_func,可以像这样被调用,但不关心v是一个std::vector,它可以是任何STL容器。有点像std::for_each:
std::vector<std::string> v = {...};
my_func(v.begin(), v.end());

但我无法理解这个函数的签名。

void my_func(??? i1, ??? i2)
{
  std::for_each(i1, i2, ...); // dumb example implementation
}

我不太擅长模板编程,所以即使看了std::for_each的函数声明也没有帮助。

这个实现是否容易,还是说使用模板变量会让它本质上变得混乱?


3
你必须使用模板。每个迭代器都是不同的类型。在C++库中,没有所有迭代器的共同超类。 - Sam Varshavchik
3个回答

13

这取决于您想要函数有多通用。如果迭代器类型必须匹配,则

template <typename T>
void my_func(T i1, T i2)
{
    std::for_each(i1,i2,...); //dumb example implementation
}

只需要这些。如果你想让它们不同,那么你只需要另一个模板参数,比如

template <typename T, typename U>
void my_func(T i1, U i2)
{
    std::for_each(i1,i2,...); //dumb example implementation
}

最后,如果您不喜欢处理模板,可以使用lambda表达式,让编译器为您处理。这将为您提供

auto my_func = [](auto i1, auto i2)
{
    std::for_each(i1,i2,...); //dumb example implementation
};

在我的使用情况(问题)中,当调用它时,我是否需要指定 T,还是编译器会推断出来? - Mr. Boy
@Mr.Boy 编译器会为您推断它。 - NathanOliver
同时,关于声明lambda对象的想法也很巧妙。 - Mr. Boy
@Mr.Boy 谢谢。Lambda表达式非常有用。它们是我最喜欢的现代C++特性之一。 - NathanOliver
实际上,使用lambda/auto,我可以将容器本身作为单个对象传递,并在其上调用.begin() / .end() - Mr. Boy
1
如果这是你想要的,你可以使用以下代码实现:template void my_func(T const & c) { std::for_each(c.begin(), c.end(),...); //愚蠢的示例实现 } - NathanOliver

8

你可以编写一个模板函数

template<typename Iterator>
void my_func(Iterator startIter, const Iterator endIter)
{
  std::for_each(startIter, endIter, /* lambda */);
}

如果想知道如何传递std::for_each的第三个参数,你可以提供另一个模板参数。
const auto defaultCallable = [](auto element){ }; // does nothing
template<typename Iterator, typename Callable = decltype(defaultCallable)>
void my_func(Iterator startIter, const Iterator endIter, Callable func = {})
{
    std::for_each(startIter, endIter, func);
}

7
语法并不是太难!下面这种方式在使用时使用了范围 for
template <template<typename...> class Iterable, typename T>
void foo(
    const Iterable<T>& y // the container
){
    for (auto&& e : y){
        // e is the 'thingy' in the container.
    }
}

你可以将任何类型的可迭代容器传递给foo函数。


3
有趣的抽象化迭代器的方法,不过不确定OP会不会认为这很“容易”(模板模板参数并不是最易理解的东西)。 - 463035818_is_not_a_number
@formerlyknownas_463035818:我修正了缩进,来吧,这很容易! - Bathsheba
如果你希望它是通用的,‘T’就需要成为一个参数包。否则,“template<typename> class Iterable”可以完成任务。但我没有看到这比只有一个“typename T”和一个“const T& y”参数更有优势。编辑:我想省略号的解决方案涵盖了像“std::vector”这样具有多个模板参数但仅第一个参数没有默认类型的情况。但一个简单的模板参数也可以解决这个问题。 - François Andrieux
2
@formerlyknownas_463035818:但它是一件美丽的事物!也许这个答案并不一定针对OP:答案并不需要如此。 - Bathsheba
2
@Bathsheba 我的担忧在于我不明白这种解决方案相对于只有一个模板参数的解决方案的好处。示例 - François Andrieux
显示剩余6条评论

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