Как обрабатывать значение по умолчанию для шаблонного функтора

Учитывая такую ​​функцию:

template<typename functor>
void foo(functor const& f)
{
    if (f)
        f(1, 2);
}

Я хочу иметь возможность назначать значение по умолчанию, где f может быть установлено на что-то похожее на NULL. Кроме того, было бы достаточно сделать фиктивный вызов пустой функции. Есть ли что-нибудь, что я могу использовать (из стандартной или boost-библиотеки), не создавая этого самостоятельно?


person 0xbadf00d    schedule 11.10.2011    source источник
comment
Не могли бы вы прояснить вопрос? Итак, вы хотите вызвать foo двумя способами: foo(some_functor); и foo();, и в последнем случае вы хотите, чтобы foo(); ничего не делал. Но зачем вообще вызывать foo();?   -  person UncleBens    schedule 11.10.2011


Ответы (7)


Используйте пустой функтор и используйте его как параметр шаблона по умолчанию. Вы не можете использовать что-то вроде NULL, потому что у вас нет указателя:

class X
{
    void operator()(...) {}
};

template <typename functor = X>
void foo(functor const& f = X())
{
    f(...);
}
person Tio Pepe    schedule 11.10.2011
comment
Я думаю, вам также нужно указать аргумент функции по умолчанию: foo(functor const & f = X()). - person Kerrek SB; 11.10.2011
comment
Полагаю, вы имеете в виду template <typename functor = X> foo(functor const & f = functor()). Это также позволяет foo<void(*)()>(). - person MSalters; 11.10.2011
comment
да @KerrekSB, ты прав. Меня путают параметры шаблона класса. Спасибо - person Tio Pepe; 11.10.2011
comment
@MSalters: Да, да: f = functor(). Спасибо! - person Kerrek SB; 11.10.2011
comment
Я не понимаю необходимости аргумента по умолчанию. Если иногда вы не собираетесь ничего передавать, а функтор ничего не делает, зачем вообще вызывать foo? - person visitor; 11.10.2011
comment
@jcalvosa - аргументы шаблона по умолчанию разрешены только в шаблоне класса. - person 0xbadf00d; 11.10.2011
comment
@ FrEEzE2046: Похоже, это изменилось в C ++ 11. - person UncleBens; 11.10.2011
comment
@UncleBens - действительно, это изменилось в C ++ 11. Но я все еще использую C ++ 03. - person 0xbadf00d; 12.10.2011
comment
Было бы неплохо избавиться от class X, используя пустое лямбда-выражение (начиная с C ++ 11), но похоже, что это возможно только начиная с C ++ 20. - person Artem Pisarenko; 01.12.2019

Либо напишите перегрузку, которая принимает и ничего не делает, либо передайте бездействующий функтор в foo.

person Lightness Races in Orbit    schedule 11.10.2011

Самый простой способ - создать перегрузку без аргументов:

template<typename functor>
void foo()
{
}

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

person Björn Pollex    schedule 11.10.2011

Я не хочу использовать перегруженные функции. Вроде бы использование самописного «ноп-класса» - лучшее решение.

struct nop
{
    void operator()(...) const volatile {}
};

template<typename functor> 
void foo(functor const& f = functor()) 
{
    f(1, 2);
}

int main()
{
    foo<nop>();
    return 0;
}
person 0xbadf00d    schedule 11.10.2011
comment
Обратите внимание, что вариативная функция-член nop не может использоваться, если при создании экземпляра вы передаете какой-либо нетривиально копируемый тип для .... - person rerx; 09.09.2014

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

struct VoidNoOp {
    void operator()() const { }
    template<class A>
    void operator()(A a) const { (void)(a); }
    template<class A, class B>
    void operator()(A a, B b) const { (void)(a); (void)(b); }
    template<class A, class B, class C>
    void operator()(A a, B b, C c) const { (void)(a); (void)(b); (void)(c); }
};

Вот вариант C ++ 11 для произвольного количества параметров:

struct VoidNoOp {
    void operator()() const { };
    template<typename P1, typename... Params>
    void operator()(P1 p1, Params... parameters) {
        (void)(p1);             // we do this just to remove warnings -- requires the recursion
        operator()(parameters...);
    }
};
person rerx    schedule 09.09.2014

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

Итак, можно ли использовать перегруженную функцию?

template<typename functor>
void foo(functor const& f)
{
       f(1, 2);
}

template<typename functor>
void foo()
{
}
person rodrigo    schedule 11.10.2011

Обертка функции, которая может сохранять NULL во время выполнения, - это std::function (C++11) или boost::function.

#include <functional>
#include <cstdio>

void foo(std::function<void(int, int)> f)
{
    if (f) {
       f(1, 2);
    }
}

void print(int a, int b) { std::printf("%d %d\n", a, b); }

int main()
{
   foo(0);
   foo(print);
}
person visitor    schedule 11.10.2011
comment
Использование оболочки функции было бы функционально адекватным, но снижение производительности не разумно. Спасибо, в любом случае. - person 0xbadf00d; 11.10.2011