2009-11-25 3 views
3

Есть ли способ получить возвращаемое значение из функции, которую я передаю в foreach.алгоритм foreach в C++

Для бывших: я,

void myfunction (int i) 
{ 
     cout << " " << i; 
} 

vector<int> myvector; 
myvector.push_back(10); 
for_each (myvector.begin(), myvector.end(), myfunction); 

Позволяет сказать, я хочу, чтобы подсчитать количество элементов в векторе, используя какое-нибудь правило, я хочу, чтобы иметь значение, возвращаемое из MYFUNCTION, возможно ли это?

+0

Не используйте для этого 'for_each'. Для этой цели существуют более совершенные алгоритмы. – jalf

ответ

5

Нет. Но вы можете сделать myfunction функтором, передать ему указатель на некоторую память и сохранить возвращаемое значение через этот указатель.

struct MyFunctor { 
    int *count; 
    MyFunctor(int *count_) : count(count_) { } 
    void operator()(int n) { 
     if (n > 5) (*count)++; 
    } 
}; 

int main() { 
    vector<int> vec; 
    for (int i=0; i<10; i++) vec.push_back(i); 
    int count = 0; 
    for_each(vec.begin(), vec.end(), Myfunctor(&count)); 
    printf("%d\n", count); 
    return 0; 
} 

Edit: Как сказано в комментариях указали, мой первый пример бы я потерпел неудачу как for_each бы сделал копию MyFunctor, поэтому мы не смогли бы извлечь возвращаемое значение из нашего исходного объекта. Я исправил стихи первоначального подхода; но вы действительно должны смотреть на решение GMan, которое является более элегантным. Я не уверен в переносимости, но он работает на моем gcc (4.4.2). И, как говорили другие, по возможности используйте то, что дает <algorithm>.

+2

Это не должно работать. Функтор копируется в 'for_each', и эта копия является той, которая считается. Вы должны использовать возвращаемое значение 'for_each'. – GManNickG

+0

Реальная альтернатива заключается в том, что MyFunctor содержит ссылку на внешний объект, таким образом, все копии имеют одну и ту же ссылку, и, следовательно, объект, указывающий на объект, обновляется правильно. –

+2

-1 Как это действительно неправильно. – UncleBens

13

Существует специальное назначение std::count (количество вхождений значения) и std::count_if (count, когда предикат возвращает true). Не злоупотребляйте std::for_each тем, для чего он не предназначался.

+1

Я думаю, что в этом конкретном случае ОП пытается дать пример («скажем») того, что он хотел бы выполнить, хотя «std :: count» полезен для этого случая, ваш ответ не показывает, как для решения более общего вопроса. –

9

for_each вернет копию функции, которую вы передали. Это означает, что вы можете сделать это:

template <typename T> 
class has_value 
{ 
    has_value(const T& pValue) : mValue(pValue), mFlag(false) {} 

    void operator()(const T& pX) 
    { 
     if (pX == mValue) 
      mFlag = true; 
    } 

    operator bool(void) const { return mFlag; } 
private: 
    T mValue; 
    bool mFlag; 
}; 

bool has_seven = std::for_each(myvector.begin(), myvector.end(), has_value<int>(7)); 

Например. Но для подсчета и т. Д., Просмотрите algorithm и посмотрите, существует ли ваша функция. (Как count)

+1

Я был на полпути через тот же код, когда появился ваш ответ, рад видеть, что я был не единственным, кто пошел прямо на это. – tzenes

+1

+1 для отличного трюка, я думаю, у меня еще есть чему поучиться о функторах – int3

+3

Это не переносимо, но нет гарантии, что 'for_each' не будет копировать функцию ввода для возвращаемого значения перед применением пройденного скопировать в диапазон ввода или не будет делать несколько копий и применять разные копии для каждого члена диапазона. –

1

std::for_each не предназначен для этого. Используйте std::count подсчитать количество элементов, равные некоторое значение, или std::count_if количество элементов, которые удовлетворяют некоторый предикат:

std::vector<SomeType> vec; 
std::count(vec.begin(), vec.end(), SomeType(9)); 
/*or */ 
bool myfunc(const SomeType& v) 
{ 
    return v == 9; 
} 
std::count_if(vec.begin(), vec.end(), f); 

Если вы просто хотите, чтобы скопировать содержимое контейнера на объект ostream как std::cout используйте зЬй :: копию вместо:

std::vector<SomeType> vec; 
... 
std::copy(vec.begin(), vec.end(), \ 
    std::ostream_iterator<SomeType>(std::cout," ")); 

Если вам необходимо возвращаемое значение из каждого вызова функции, используйте std::transform:

std::vector<SomeType> src; 
std::vector<SomeType> result; 
int myfunc(int val) 
{ 
    ... 
} 
std::transform(src.begin(), src.end() \ 
    result.begin(), myfunc); 

std::transform также перегружен, поэтому он работает как для двоичных функций, так и для унарных функций.

0

Это выполнимо:

int main() 
{ 
    std::vector<int> v; 
    for (int i = 1; i <= 10; i++) 
    v.push_back(i); 

    int hits = 0; 
    CountEven evens(&hits); 
    std::for_each(v.begin(), v.end(), evens); 
    std::cout << "hits = " << hits << std::endl; 

    return 0; 
} 

Но посмотрите на противной реализации CountEvens:

class CountEven { 
    public: 
    CountEven(int *hits) : hits(hits) {} 
    CountEven(const CountEven &rhs) : hits(rhs.hits) {} 
    void operator() (int n) { if (n % 2 == 0) ++*hits; } 

    private: 
    int *hits; 
}; 

Обратите внимание, что конструктор копирования приводит несколько экземпляров, чтобы один и тот же указатель.

Использование std::count или std::count_if.

3

Если вам нужен мощный foreach, то BOOST_FOREACH makro. Кроме того, boost - это в основном библиотека заголовков, поэтому вы можете включить в проект только boost_foreach.hpp (afair). Пример:

BOOST_FOREACH(int & i , my_vector) 
{ 
    i = 0; 
} 

My_vector может быть vector<int> или int[] или любой другой вид итератора.

+0

Хорошая альтернатива, хотя мне не нравится макрос, это так удобно. –

1

Хорошо, я боюсь, что вы выбрали ваш пример плохо, когда вы подобрали проблему подсчета ...

Проблема заключается в том, что for_each является весьма общим и существуют более конкретные алгоритмы для конкретной реализации (count, accumulate, transform , ...)

Итак, давайте возьмем еще один пример: for_each обычно используется для применения мутирующей операции на объектах, которые он рассматривает. Это не мешает вам собирать статистику при этом.

Мы должны позаботиться, хотя for_each действительно возвращает объект Predicate, и нет гарантии, что этот объект использовался для каждого предмета в диапазоне. Реализация бесплатна, чтобы скопировать предикат вокруг и использовать копии на части диапазона ... так что копия, которую вы окончательно вернули, может быть удалена.

class Predicate 
{ 
public: 
    Predicate(size_t& errors) : m_errors(errors) {} 
    void operator()(MyObject& o) 
    { 
    try { /* complicated */ } catch(unfit&) { ++m_errors; } 
    } 
private: 
    size_t& m_errors; 
}; 

std::vector<MyObject> myVec; 
// fill myVec 

size_t errors = 0; 
std::for_each(myVec.begin(), myVec.end(), Predicate(errors)); 

Хитрость здесь в том, что все копии исходного предиката будет указывать на то же size_t переменной, таким образом, эта переменная была правильно обновлена.

1

Вы можете адаптировать std::for_each для этого, как показал GMan.

Но лучшим решением является использование правильного алгоритма.

Вы должны иметь возможность использовать std::count или std::count_if, или, возможно, std::accumulate. Это позволяет вам вернуть один результат для обработки всей последовательности.

В качестве альтернативы std::transform позволяет вам возвращать результат для каждого элемента в последовательности, создавая новую последовательность результатов, содержащую результаты.