2013-05-14 4 views
3

Проще говоря, если у меня есть набор и вектор, как мне создать общий метод, который может обрабатывать оба параметра.Как перебирать общие коллекции в C++?

Все, что я хочу сделать, это перебирать любые типы коллекций. Похоже, это должно быть тривиально, но я чего-то не хватает.

void printMeSomeStrings(somebaseclass<string> strings) { 
    for (auto& str : strings) { 
    cout << str << endl; 
    } 
} 

В C# я бы передал IEnumerable или что-то в этом роде. Затем я мог перебирать коллекцию.

Любое общее чтение, объясняющее ответ, будет оценено по достоинству.

ответ

1

Первый вариант поставить код делает на итерации в шаблоне. Это требует раскрытия реализации всем, кто ее использует, что имеет недостатки.

В принципе, возьмите тип C в качестве параметра template, а затем напишите свой код в терминах этого типа C.

template<typename C> 
void printMeSomeStrings(C&& strings) { 
    for (auto const& str : strings) { 
    cout << str << endl; 
    } 
} 

Если вы хотите быть в состоянии иметь сильный барьер между интерфейсом и реализацией, C++ 11 подхода будет заниматься типа стирания на for -iterable контейнера, а затем выставить for -iterable контейнер, как, как работает std::function.

Это сложнее. Я лично считаю, что писать функцию for_each проще, чем писать полностью переработанный итерационный адаптер. Если вы хотите, чтобы объект стирания с полным инерционным контейнером целился, начинайте с boost или спросите меня ниже, и я могу это сделать.

for_each адаптер легкий, однако.

#include <functional> 
#include <utility> 
#include <iterator> 
#include <memory> 

template<typename T> 
struct for_each_helper_interface { 
    virtual ~for_each_helper_interface() {} 
    virtual void for_each(std::function< void(T) > const&) = 0; 
}; 
template<typename C, typename T> 
struct for_each_helper:for_each_helper_interface<T> { 
    C& c; 
    for_each_helper(C& in):c(in) {} 
    virtual void for_each(std::function< void(T) > const& f) override final { 
    for(auto&& x:c) { 
     f(x); 
    } 
    } 
}; 
template<typename T> 
struct for_each_adaptor { 
    std::unique_ptr<for_each_helper_interface<T>> pImpl; 
    void for_each(std::function< void(T) > const& f) { 
    if (pImpl) { 
     pImpl->for_each(f); 
    } 
    } 
    template<typename C> 
    for_each_adaptor(C&& c): pImpl(new for_each_helper<C, T>(std::forward<C>(c))) {} 
}; 

, которые наберут стертый контейнер T (или тип кабриолета в T!) И выставить for_each метод, который позволяет перебирать содержимое контейнера. Используйте так:

#include <set> 
#include <iostream> 
#include <vector> 
void print_stufF(for_each_adaptor<std::string const&> c) { 
    c.for_each([&](std::string const&s){ 
    std::cout << s << "\n"; 
    }); 
} 
int main() { 
    std::set<std::string> s; 
    s.insert("hello"); 
    s.insert("world"); 
    print_stuff(s); 
    std::vector<std::string> v; 
    v.push_back("hola"); 
    v.push_back("bola"); 
    print_stuff(v); 
} 

Что происходит здесь в том, что для каждого типа используется для построения нашего адаптера, мы строим пользовательскую реализацию для каждого. Затем мы сохраняем указатель на абстрактный базовый класс этого настраиваемого класса и перенаправляем для каждого обращения к нему.

Это означает, что все, что специализируется на std::begin или определяет его собственное начало, не обязательно должно быть связано: вместо этого мы создаем специальные отношения в точке использования.

Живой пример: http://ideone.com/xOqBkI

+1

У Boost уже есть 'any_range', который будет стирать тип, подобный' std :: function'. –

4

Вы можете использовать шаблоны. Например:

#include <iostream> 

template<typename C> 
void foo(C const& c) 
{ 
    std::cout << "{ "; 
    for (auto const& x : c) 
    { 
     std::cout << x << " "; 
    } 
    std::cout << "}"; 
} 

А вот как вы бы использовать:

#include <set> 
#include <vector> 

int main() 
{ 
    std::vector<int> v = {1, 2, 3}; 
    foo(v); 

    std::cout << std::endl; 

    std::set<std::string> s = {"Hello,", "Generic", "World!"}; 
    foo(s); 
} 

Live example.

+0

хороший ответ, если вы хотите избежать определения своего шаблона, вы можете использовать std :: for_each с функцией лямбда или принтера. – Alex

3

Это точно, что итераторы были разработаны для.

template <class It> 
void print_some_strings(It first, It last) { 
    std::cout << *first++ << std::endl; 
} 
0

В C#, я бы передать IEnumerable или что-то подобное.

C++ использует более питонический подход утиного ввода для определения интерфейсов (обычно называемых концепцией в C++), а не использования наследования. Для того, чтобы сделать утку набрав в C++, можно использовать шаблонную функцию так:

template<typename C> 
void printMeSomeStrings(const C& strings) 
{ 
    for (const auto& str : strings) 
    { 
     cout << str << endl; 
    } 
} 

В питоне, утиная типизация делаются во время выполнения, но в C++ это делается во время компиляции, поэтому нет затрат времени выполнения утка печатать, и все проверяется во время компиляции.

Подробнее о C++, чтобы получить информацию. Во-первых, эквивалент IEnumerator<T> является итератором на C++. Here - это страница о различных категориях итераторов, а также то, что должно быть реализовано для итераторов. По устаревшим причинам итераторы моделируются после указателей на C, что позволяет использовать C-массивы со стандартными алгоритмами C++.

Однако, в отличие от IEnumerator<T>, итераторы должны попадать парами. Итератор к началу и концу (который один за последним элементом). Таким образом, эквивалент IEnumerable<T> в C++ называется диапазоном. В C++ 11 диапазон определяется двумя свободными функциями: begin(T) и end(T) (он также может быть реализован как функция-член .begin() и .end()).

Определяя концепцию (aka interface) как две бесплатные функции, в отличие от использования наследования, диапазоны могут быть реализованы неинтрузивно. Так, например, если у вас есть работа с некоторым api-наследием, использующим связанные с C стили. Теперь они могут быть адаптированы как диапазон C++ 11 и использоваться внутри цикла C++.

+0

Это не утка набрав - это вывод типа. –

+0

@MichaelBurge Его обычно называют компиляцией утиного набора текста в сообществе C++. См. [Здесь] (http://www.drdobbs.com/templates-and-duck-typing/184401971) –