slarkevich
@slarkevich

Можно ли передать в функцию-предикат значение как в лямбду?

Например, нужно найти количество элементов вектора v, которые больше N. В случае с лямбдами можно написать так:
int N = 5;
count_if(begin(v), end(v), [N](int x) { return x > N; });


Но если предположить, что функция достаточно большая и оформлять ее через лямбду неудобно?
Тогда хотелось бы написать что-то вроде этого:
bool GreatherThanN(int x) {
    if (x > N) {
        return true;
    }
    return false;
}

count_if(begin(v), end(v), GreatherThanN);

Как в этом случае заставить функцию GreatherThanN увидеть это самое N?
  • Вопрос задан
  • 347 просмотров
Решения вопроса 3
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
Как в этом случае заставить функцию GreatherThanN увидеть это самое N?

Если функции нужен контекст, то вместо функции можно использовать функциональный объект -- объект класса с оператором ():

class GreaterThanN {
    int N;
public:
    GreaterThanN(int _N): N(_N)
    {
    }
    bool operator ()(int x) const
    {
        if (x > N)
            return true;
        return false;
    }
};

int N = 5;
count_if(begin(v), end(v), GreatherThanN(N));
Ответ написан
@MarkusD Куратор тега C++
все время мелю чепуху :)
Лямбда - это объект неименованного типа, не являющегося типом объединеня или агрегатным типом.
Одним из свойств лямбды является то, что для случая с пустым замыканием лямбда неявно приводится к указателю на глобальную функцию. В ином случае лямбда - это объект-функтор, у которого определен operator().

Те свойства, что ты хочешь получить от глобальной функции, называются контекстом функции - это список глобальных по отношению к пространству функции объектов, изменение которых приводит к изменению поведения функции.

Контекст можно расположить, скажем, в глобальном пространстве имен в виде глобальных переменных.
int N = 0;
bool GreatherThanN(int x) {
    if (x > N) {
        return true;
    }
    return false;
}

Такой подход, мягко говоря, неидеален. Его просто стоит знать, как врага, в лицо. :)

Другим способом является определение контекста через параметры шаблона функции:
template< int N >
bool GreatherThanN(int x) {
    if (x > N) {
        return true;
    }
    return false;
}

Инстанцирование функции из такого шаблона пройдет через оптимизации и с высокой вероятностью выродится в константу в месте вызова. Но N в этом случае надо знать на этапе компиляции. Да и в целом, не всё можно определить через параметры шаблона. Параметром шаблона может быть или тип, или константа целочисленного типа или типа сводимого к целочисленному (enum, bool, const char*, известные на этапе компиляции указатели).

Третьим случаем определения контекста является использование т.н. функторов - функциональных объектов.
Контекст в этом случае укладывается в объекте, а сама функция заключается в operator().
struct Comparator final
{
	int N;
	
	inline const bool operator () ( int x ) const
	{
		return x > N;
	}
};

// ...

Comparator GreatherThanN{ 10 };

// ...

count_if(begin(v), end(v), GreatherThanN);

Но с появлением лямбд в C++11 такой подход резко убавил в популярности, т.к. его задачи теперь с большим успехом решают лямбды.
Ответ написан
Комментировать
vt4a2h
@vt4a2h Куратор тега C++
Senior software engineer (C++/Qt/boost)
Через лямбду много чего удобно оформлять. Просто используйте другое форматирование.
count_if(begin(v), end(v), [N](int x) { 
   // ...
   // ...
   return x > N; 
});

// Or
auto greaterThan = [N](int x) { 
   // ...
   // ...
   return x > N; 
}
count_if(begin(v), end(v), greaterThan);


Этот код компилируется примерно в то, что вам написали выше (объект с методом) и отлично отлаживается. Просто выглядит короче и не требует введения новых сущностей. Во многих случаях это отлично решение. Если надо переиспользовать в других частях кода, то просто вынесете нужные части свободными функциями.

bind в современном C++ использовать не надо.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@laphroaig
Еще можно bind использовать

using namespace std;
bool GreatherThanN(int N, int x) {
    if (x > N) {
        return true;
    }
    return false;
}

using namespace std::placeholders;

int N = 5;
count_if(begin(v), end(v), bind(GreatherThanN, 5, _1) );
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы