Перебор выходных данных функции-члена в std::для каждого
У меня есть класс с функцией-членом accessor, которую я хочу вызвать и применить результат к функтору с помощью std:: for_each. У меня есть рабочая версия ниже, которая использует цикл for и for_each, но версия for_each является загадочной и громоздкой. Есть ли способ сделать версию for_each более лаконичной, учитывая, что у меня есть доступ к boost, но не C++11?
#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 вообще-то, но я нашел это решение, и совершенно новый вопрос не имел особого смысла.
Спасибо, вся помощь действительно ценится!
5 ответов:
Если у вас нет C++11, но Boost, вы можете попробовать выражение
bind()
(которое также будет работать с C++2011, посколькуbind()
является частью C++2011):std::for_each(a.begin(), a.end(), bind(&avg<value_type>, bind(&Value<value_type>::getValue, _1)));
Если вы используете 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 }
Попытка быть слишком умным с объектом функции и тому подобное часто может иметь обратный эффект, скрывая ваш код.
Один из способов сделать его более аккуратным-использовать Boost.Феникс . Вы можете сократить до этого:
std::for_each(values.begin(), values.end(), lazy(avg)(arg1.getValue()));
Вот как это сделать. Первое, что вам нужно сделать, это сделать объект функции
avg
ленивым. Простейший путь к этому-на месте с функцией, определенной следующим образом:template<class Function> function<Function> lazy(Function x) { return function<Function>(x); }
Следующее, что вам нужно сделать, это написать объект функции для getValue, который может быть ленивым, например:
В-третьих, мы расширяем актеров Феникса, используя наш класс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()));
Кредит идет Матиасу гонару на повышение.список рассылки пользователей для указания мне на это решение:
std::for_each(values.begin(), values.end(), boost::bind(boost::ref(avg), boost::bind(&value_wrapper_type::getValue, _1)) );
Обертывание
avg
сboost::ref
требуется, потому что в противном случае копияavg
заполняется результатами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; }
Если вы можете использовать boost, но не функции C++11, то я бы рассмотрел использование макроса BOOST_FOREACH
Да, это макрос, но в качестве макросов он хорошо себя ведет
Это также читается довольно хорошо и трудно ошибиться
BOOST_FOREACH(const Value& rValue, values) { avg(rValue.getValue()); }
C++11 range based for loops заменит его