2012-02-29 4 views
2

У меня есть класс с функцией-членом-членом, которую я хочу вызвать и применить результат к функтору, используя std :: for_each. У меня есть рабочая версия ниже, которая использует цикл for и for_each, но версия for_each является загадочной и громоздкой. Есть ли способ сделать версию for_each более кратким, учитывая, что у меня есть доступ к boost, но не C++ 11?Итерация над выходом функции-члена в std :: for_each

#if 0 
    // for loop version: 
    for(value_vector_type::iterator it = values.begin(); it!=values.end(); it++){ 
    avg(it->getValue()); // I want to put this in a for_each loop 
    } 
#else 
    // bind version: 
    std::for_each(values.begin(), values.end(), // iterate over all values 
    boost::bind(
     boost::mem_fn(&average_type::operator()), // attach the averaging functor to the output of the getvalue call 
     &avg, 
     boost::bind(
     boost::mem_fn(&value_wrapper_type::getValue), // bind the getValue call to each element in values 
     _1 
    ) 
    ) 
); 
#endif  

Вот полная рабочая реализация:

#include <vector> 
#include <algorithm> 
#include <iostream> 
#include <boost/bind.hpp> 
#include <boost/bind/mem_fn.hpp> 

// A value wrapper 
template<typename T> 
struct Value { 
    Value(){} 
    Value(const T& value, bool valid = true):m_value(value),m_valid(valid){} 

    T getValue(){ return m_value; } 
    bool getValid(){ return m_valid; } 
    void setValue(const T& value){ m_value = value; } 
    void setValid(const T& valid){ m_valid = valid; } 

private: 
    T m_value; 
    bool m_valid; 
}; 

// Class that calculates the average piecewise 
template<typename T> 
struct Average { 
private: 
    T m_numPoints; 
    T m_ChannelSum; 

public: 

    Average() : m_numPoints(0), m_ChannelSum(0.0){} 

    void operator()(T value){ 
     m_numPoints++; 
     m_ChannelSum+=value; 
    } 

    double getAverage(){ return m_ChannelSum/m_numPoints; } 
    T getCount(){ return m_numPoints; } 
    T getSum(){ return m_ChannelSum; } 
}; 

// Run the average computation on several values 
int main(int argc, char** argv){ 
    typedef int value_type; 
    typedef Value<value_type> value_wrapper_type; 
    typedef std::vector<value_wrapper_type> value_vector_type; 
    value_vector_type values; 
    values.push_back(value_wrapper_type(5)); 
    values.push_back(value_wrapper_type(7)); 
    values.push_back(value_wrapper_type(3)); 
    values.push_back(value_wrapper_type(1)); 
    values.push_back(value_wrapper_type(2)); 

    typedef Average<value_type> average_type; 
    average_type avg; 

#if 0 
    // for loop version: 
    for(value_vector_type::iterator it = values.begin(); it!=values.end(); it++){ 
    avg(it->getValue()); // I want to put this in a for_each loop 
    } 
#else 
    // bind version: 
    std::for_each(values.begin(), values.end(), // iterate over all values 
    boost::bind(
     boost::mem_fn(&average_type::operator()), // attach the averaging functor to the output of the getvalue call 
     &avg, 
     boost::bind(
     boost::mem_fn(&value_wrapper_type::getValue), // bind the getValue call to each element in values 
     _1 
    ) 
    ) 
); 
#endif  
    std::cout << "Average: " << avg.getAverage() << " Count: " << avg.getCount() << " Sum: " << avg.getSum() << std::endl; 
} 

примечания: мой первоначальный вопрос, как построить for_each на всех, но я нашел, что решение и целый новый вопрос не делало много смысла.

Спасибо, вся помощь действительно оценена!

+0

Почему? Что это значит, что 'std :: accumulate (values.begin(), values.end())/values.size();' не будет делать? –

+0

@JerryCoffin Усреднение - это упрощенная проблема, которая извлекает суть моей актуальной проблемы, которая связывает функцию-член с функтором в цикле for_each. –

+0

Моя точка зрения также должна была быть более широкой - я думаю, что вы принимаете неправильный подход, и вам будет лучше спросить о том, что вы на самом деле пытаетесь сделать здесь, а не о деталях (возможно, IMO), который вы начали предпринимать для достижения этого. –

ответ

0

Заслуга Матиасу Gaunard на boost.users список рассылки для расшивки меня к этому решению:

std::for_each(values.begin(), values.end(), 
    boost::bind(boost::ref(avg), boost::bind(&value_wrapper_type::getValue, _1)) 
); 

Оберточные avg с boost::ref требуется, потому что в противном случае копия avg заполняется с результатами O f getValue(), а не avg.

Вот полный скомпилирован и проверенное решение:

#include <stdexcept> 
#include <vector> 
#include <algorithm> 
#include <iostream> 
#include <boost/bind.hpp> 
#include <boost/bind/mem_fn.hpp> 

// A value wrapper 
template<typename T> 
struct Value { 
    Value(){} 
    Value(const T& value, bool valid = true):m_value(value),m_valid(valid){} 

    T getValue(){ return m_value; } 
    bool getValid(){ return m_valid; } 
    void setValue(const T& value){ m_value = value; } 
    void setValid(const T& valid){ m_valid = valid; } 

private: 
    T m_value; 
    bool m_valid; 
}; 

// Class that calculates the average piecewise 
template<typename T> 
struct Average { 
private: 
    T m_numPoints; 
    T m_ChannelSum; 

public: 
    typedef void result_type; 

    Average() : m_numPoints(0), m_ChannelSum(0.0){} 

    result_type operator()(T value){ 
     m_numPoints++; 
     m_ChannelSum+=value; 
    } 

    double getAverage(){ 
    if (m_ChannelSum==0) { 
     throw std::logic_error("Cannot get average of zero values"); 
    } 

    return m_ChannelSum/m_numPoints; 
    } 
    T getCount(){ return m_numPoints; } 
    T getSum(){ return m_ChannelSum; } 
}; 

// Run the average computation on several values 
int main(int argc, char** argv){ 
    typedef int value_type; 
    typedef Value<value_type> value_wrapper_type; 
    typedef std::vector<value_wrapper_type> value_vector_type; 
    value_vector_type values; 
    values.push_back(value_wrapper_type(5)); 
    values.push_back(value_wrapper_type(7)); 
    values.push_back(value_wrapper_type(3)); 
    values.push_back(value_wrapper_type(1)); 
    values.push_back(value_wrapper_type(2)); 

    typedef Average<value_type> average_type; 
    average_type avg; 

#if 0 
    // for loop version: 
    for(value_vector_type::iterator it = values.begin(); it!=values.end(); it++){ 
    avg(it->getValue()); // I want to put this in a for_each loop 
    } 
#else 
    // bind version: 
    std::for_each(values.begin(), values.end(), 
    boost::bind(boost::ref(avg), boost::bind(&value_wrapper_type::getValue, _1)) 
); 
#endif  
    std::cout << "Average: " << avg.getAverage() << " Count: " << avg.getCount() << " Sum: " << avg.getSum() << std::endl; 
} 
2

если вы используете C++ 11, то вы можете попробовать

for(auto& a: values) 
    avg(a->getValue()); 

или

std::for_each(a.begin(), a.end(), [](whatever_type& wt){ 
    avg(wt->getValue()); 
}); 

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

for(value_vector_type::iterator it = values.begin(); 
    it!=values.end(); 
    ++it) 
{ 
    avg(it.getValue()); // I want to put this in a for_each loop 
} 

Стараясь быть слишком умным с объектом функции и т.п., часто может иметь обратный эффект затемнения кода.

+1

Обычно рекомендуется увеличивать итераторы с помощью приращения приращения, как '++ it'. –

+0

@GregHewgill соглашается, я этого не видел. исправлено – 111111

+0

также '->' должно быть '.' (' a' и 'wt' являются ссылками нетераторами) – Philipp

2

Если вы не C++ 11, но Повысьте вы можете попробовать bind() выражение (которое также будет работать с C++ 2011 в bind() является частью C++ 2011):

std::for_each(a.begin(), a.end(), bind(&avg<value_type>, bind(&Value<value_type>::getValue, _1))); 
+1

Я думаю, что это должно быть 'bind (avg, bind (& Value :: getValue, _1))'. –

+0

Er, yes: Я не заметил, что 'Value' является шаблоном. Кроме того, может потребоваться взять адрес 'avg' в дополнение к указанию аргумента шаблона. Я обновлю ответ ... –

+0

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

1

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

std::for_each(values.begin(), values.end(), lazy(avg)(arg1.getValue())); 

Heres, как это сделать. Первое, что вам нужно сделать, это сделать объект функции avg ленивым.Самый простой способ, который находится в месте с функцией, определяется следующим образом:

template<class Function> 
function<Function> lazy(Function x) 
{ 
    return function<Function>(x); 
} 

Следующая вещь, которую вам нужно сделать, это написать функциональный объект для ПолучитьЗначение, что может быть ленивым, как это:

struct get_value_impl 
{ 
    // result_of protocol: 
    template <typename Sig> 
    struct result; 

    template <typename This, typename T> 
    struct result<This(Value<T>&)> 
    { 
     // The result will be T 
     typedef typename T type; 
    }; 

    template <typename V> 
    typename result<get_value_impl(V &)>::type 
    operator()(V & value) const 
    { 
     return value.getValue(); 
    } 
}; 

в-третьих, мы расширим актеров феникса, используя наш get_value_impl класс, поэтому он будет иметь метод getValue, как это:

template <typename Expr> 
struct value_actor 
    : actor<Expr> 
{ 
    typedef actor<Expr> base_type; 
    typedef value_actor<Expr> that_type; 

    value_actor(base_type const& base) 
     : base_type(base) {} 

    typename expression::function<get_value_impl, that_type>::type const 
    getValue() const 
    { 
     function<get_value_impl> const f = get_value_impl(); 
     return f(*this); 
    } 
}; 

Наконец, мы все это вместе б у определение аргумента и передать его в алгоритм for_each:

expression::terminal<phoenix::argument<1>, value_actor> arg1; 
std::for_each(values.begin(), values.end(), lazy(avg)(arg1.getValue())); 
+0

Очень интересная техника. Мне нужно будет потратить некоторое время, чтобы понять Boost.Phoenix. Благодаря! –

0

Если вы можете использовать импульс, но не C++ 11 функций, то я бы рассмотреть возможность использования the BOOST_FOREACH macro

Да, это макрос, но поскольку макросы идут это хорошо себя

он также читает довольно красиво и трудно получить неправильно

BOOST_FOREACH(const Value& rValue, values) 
{ 
    avg(rValue.getValue()); 
} 

C++ 11 диапазон основе для петель будет заменить его