Функция усиления и лямбда, чтобы обернуть функцию
Я хочу преобразовать этот простой код:
void setZComp(Imath::V3f& pt)
{
pt.z = 0.0;
}
int myfunc()
{
...
std::vector<Imath::V3f> vec(5,Imath::V3f(1.0,1.0,1.0));
std::for_each(vec.begin(),vec.end(),boost::bind(&setZComp,_1));
...
}
к чему-то в этом роде, для того, чтобы setZComp не был объявлен снаружи, а был встроен
int myfunc()
{
...
boost::function<double(Imath::V3f&)> f = (boost::lambda::_1 ->* &Imath::V3f::z = 0.0) ;
std::for_each(vec.begin(),vec.end(),boost::bind(&f,_1));
...
}
Я совсем новичок в Boost Bind и Lambda, и я не знаю, можно ли это как-то сделать. Очевидно, что приведенный выше код не работает.
6 ответов
Если вы не можете использовать лямбду C++11, то вы можете использовать boost::lambda::bind
,
Так что в вашем случае что-то вроде следующего:
boost::lambda::bind(&Imath::V3f::z, boost::lambda::_1) = 0.0
Полный пример, так как я не знаю ваших внутренних органов:
struct S
{
S():i(0){};
int i;
};
int main()
{
std::vector<S> vec;
vec.push_back(S());
std::for_each(vec.begin(), vec.end(), boost::lambda::bind(&S::i, boost::lambda::_1) = 5);
std::cout << vec.front().i << std::endl; // outputs 5
return 0
}
Вы используете кувалду, чтобы сломать орех? Иногда я думаю, что проще просто использовать нормальный цикл for и установить переменную в явном виде самостоятельно. Это делает код намного проще для чтения и обслуживания.
typedef std::vector<Imath::V3f> V3fVector;
V3fVector vec(5,Imath::V3f(1.0,1.0,1.0));
for (V3fVector::iterator i = vec.begin(), iEnd = vec.end(); iEnd != i; ++i)
i->z = 0.0;
Как бы ни был полезен boost bind, это также синтаксический беспорядок, который делает простой код нечитаемым.
Как объяснено в разделе Переменные-члены как цели:
Указатель на переменную-член на самом деле не функция, а первый аргумент для [
boost::lambda::bind
] функция, тем не менее, может быть указателем на переменную-член. Вызов такого выражения связывания возвращает ссылку на элемент данных.
Таким образом, чтобы построить лямбда-выражение, которое обращается к z
член, вы можете использовать:
boost::lambda::bind(&Imath::V3f::z, boost::lambda::_1)
Возвращенный объект может сам использоваться в других выражениях. Например,
boost::lambda::bind(&Imath::V3f::z, boost::lambda::_1) = 0.0
означает "получить double
ссылка на z
член первого аргумента (тип Imath::V3f&
) и присвойте значение 0.0".
Затем вы можете использовать эту лямбду с Boost.Function и std::for_each
:
boost::function<void(Imath::V3f&)> f = boost::lambda::bind(&Imath::V3f::z, boost::lambda::_1) = 0.0;
std::for_each(vec.begin(), vec.end(), f);
Для справки, вот полный, компилируемый пример:
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <boost/function.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
namespace Imath
{
class V3f
{
public:
double x, y, z;
V3f(double x_, double y_, double z_)
: x(x_), y(y_), z(z_)
{
}
friend std::ostream& operator<<(std::ostream& os, const V3f& pt) {
return (os << '(' << pt.x << ", " << pt.y << ", " << pt.z << ')');
}
};
}
int main()
{
std::vector<Imath::V3f> vec(5, Imath::V3f(1.0, 1.0, 1.0));
boost::function<void(Imath::V3f&)> f = boost::lambda::bind(&Imath::V3f::z, boost::lambda::_1) = 0.0;
std::for_each(vec.begin(), vec.end(), f);
std::vector<Imath::V3f>::iterator it, end = vec.end();
for (it = vec.begin(); it != end; ++it) {
std::cout << *it << std::endl;
}
return EXIT_SUCCESS;
}
Выходы:
(1, 1, 0) (1, 1, 0) (1, 1, 0) (1, 1, 0) (1, 1, 0)
Вы могли бы также рассмотреть возможность взглянуть на boost::phoenix. Я думаю, что это более полная реализация функционального программирования для C++, чем лямбда-библиотека.
Если вы хотите использовать boost::lambda, я иногда нахожу более понятным объявление переменной "указатель на член" непосредственно перед строкой, содержащей лямбду, которая затем позволяет вам использовать оператор ->* вместо boost:: лямбда:: привязывать.
Однако, как указал Алан, простой цикл здесь может быть самым простым решением. Используйте BOOST_FOREACH, чтобы сделать его еще проще.
Вот модифицированная версия примера реализации mkaes, в которой вместо bind используется оператор ->*, а также показано, как использовать BOOST_FOREACH в качестве альтернативы.
#include <iostream>
#include <vector>
#include <boost/lambda/lambda.hpp>
#include <boost/foreach.hpp>
// I like to provide alternate names for the boost::lambda placeholders
boost::lambda::placeholder1_type& arg1 = boost::lambda::_1 ;
boost::lambda::placeholder2_type& arg2 = boost::lambda::_2 ;
boost::lambda::placeholder3_type& arg3 = boost::lambda::_3 ;
struct S
{
S():i(0){};
int i;
};
int main()
{
std::vector<S> vec;
vec.push_back(S());
// Define this pointer-to-member so we can
// use it in the lambda via the ->* operator
int S::* i = &S::i ;
std::for_each(vec.begin(), vec.end(), &arg1->*i = 5);
std::cout << vec.front().i << std::endl; // outputs 5
// Alternatively, just use a simple foreach loop
BOOST_FOREACH( S & s, vec )
{
s.i = 6 ;
}
std::cout << vec.front().i << std::endl; // outputs 6
return 0 ;
}
Если у вас есть доступ к последней версии g++
с поддержкой C++11 или MSVC 2010 вы можете сделать следующее:
int myfunc()
{
...
std::for_each(vec.begin(),vec.end(),[](Imath::V3f& pt){ pt.z = 0.0; });
...
}