C++:如何将任何可迭代类型作为函数参数传递

ayd*_*dow 7 c++ iterable generic-type-argument

作为练习,我正在尝试join()用C++ 实现Python的方法.我最终会将该函数添加为std::string类的方法,但我认为让它工作更为重要.我已经定义了如下函数:

template<typename Iterable>
std::string join(const std::string sep, Iterable iter);
Run Code Online (Sandbox Code Playgroud)

有什么方法可以确保Iterable类型实际上是可迭代的吗?例如,我不想收到int或char..

Jus*_*tin 7

在C++中,Iterable我们将迭代器(几乎是一个指针)传递给范围的前端和末端,而不是一个迭代器:

template<typename Iter>
std::string join(const std::string &sep, Iter begin, Iter end);
Run Code Online (Sandbox Code Playgroud)

请注意,sep应该作为const 引用传递,因为您不需要复制它.

但是,您不必担心Iter它实际上是否是迭代器.这是因为如果代码不起作用,代码将无法编译.

例如,假设您是这样实现的(这是一个糟糕的实现):

template<typename Iter>
std::string join(const std::string &sep, Iter begin, Iter end) {
    std::string result;

    while (begin != end) {
        result += *begin;
        ++begin;
        if (begin != end) result += sep;
    }

    return result;
}
Run Code Online (Sandbox Code Playgroud)

然后传入的类型Iter必须有一个operator++,一个operator!=和一个operator*工作,这是一个很好理解的迭代器契约.

  • *“不是传递一个`Iterable`,而是传递一个迭代器” *-嗯,我们不必这样做。Stepanov决定采用这种方法时,使stl算法非常冗长。Andrei Alexandrescu谈到了基于范围算法的优点,而不是基于迭代器的优点。 (2认同)
  • 我不同意(并且我不打算进行扩展讨论).我非常尊重标准库,但在编写自己的新实用程序时,我遵循我认为优越的方法. (2认同)

W.F*_*.F. 7

所有标准的 C++ 集合都有begin()和end()成员函数。您可以利用这一事实来确保传递的参数实际上是某些SFINAE(c++11 示例)的集合(在您的术语中 - 可迭代):

#include <array>
#include <list>
#include <vector>
#include <map>
#include <string>

template <class Iterable>
auto join(const std::string sep, const Iterable& iterable) -> decltype(iterable.begin(), iterable.end(), std::string{}) {
    (void)sep; // to suppress warning that sep isn't used
    // some implementation
    return {};
}

int main() {
    join(";", std::array<int, 5>{});
    join(";", std::list<int>{});
    join(";", std::vector<float>{});
    join(";", std::string{});
    join(";", std::map<int, float>{});
    //join(";", int{}); // does not compile as int is not a collection
}
Run Code Online (Sandbox Code Playgroud)

[现场演示]