Как правильно проверить, пусто ли std :: function в C ++ 11?

99

Мне было интересно, как правильно проверить std::function, пуст ли. Рассмотрим этот пример:

class Test {
    std::function<void(int a)> eventFunc;

    void registerEvent(std::function<void(int a)> e) {
        eventFunc = e;
    }

    void doSomething() {
        ...
        eventFunc(42);
    }
};

Этот код отлично компилируется в MSVC, но если я позвоню doSomething()без инициализации, eventFuncкод явно выйдет из строя. Это ожидалось, но мне было интересно, какова ценность eventFunc? Отладчик говорит 'empty'. Поэтому я исправил это, используя простой оператор if:

   void doSomething() {
        ...
        if (eventFunc) {
            eventFunc(42);
        }
   }

Это работает, но мне все еще интересно, каково значение неинициализированного std::function? Я хотел бы написать, if (eventFunc != nullptr)но std::functionэто (очевидно) не указатель.

Почему чистый, если работает? Что за магия стоит за этим? И правильно ли это проверить?

НайтЭлфик
источник
8
Обратите внимание, что eventFuncэто не лямбда; это std::function. Вы можете хранить лямбды в std::functions, но это не одно и то же.
templatetypedef
3
Вы правы, я изменил название, чтобы не было путаницы. Спасибо.
NightElfik

Ответы:

107

Вы проверяете не пустую лямбду, а то, std::functionесть ли в ней вызываемая цель. Проверка четко определена и работает, поэтому std::function::operator boolдопускает неявное преобразование boolв контекст, в котором требуются логические значения (например, условное выражение в ifоператоре).

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


Вы также можете написать, if(eventFunc != nullptr)если хотите, это эквивалентно коду, который у вас есть в вопросе. std::function определяет operator== и operator!=перегружает для сравнения с nullptr_t.

Преторианец
источник
1
Но разве == nullptrне то же самое? Похоже, что для ==оператора должна быть перегрузка, из-за которой «пустой» std::functionсравнивается trueс nullptr: cplusplus.com/reference/functional/function/operators
Кайл Стрэнд
3
@KyleStrand Да, сравнение с nullptrбудет тоже работать, if(eventFunc != nullptr)эквивалентно if(eventFunc)указанному выше вопросу.
Praetorian
3
Технически std::function::operator boolне позволяет неявное преобразование в bool. В explicitконце концов, он отмечен , но стандарт делает исключение для определенных языковых конструкций, которые ожидают логические выражения, называя их «контекстно преобразованными в логические». Вы можете найти соответствующий фрагмент стандартного языка и объяснение здесь: chris-sharpe.blogspot.com/2013/07/…
bcrist
@bcrist Да, я знаю, что есть оператор логического преобразования explicit, поэтому я был осторожен, чтобы указать, что позволяет неявное преобразование boolв контекстах, где требуются логические значения . Именно это и происходит в рассматриваемом коде.
Praetorian
5
@Praetorian Я пытаюсь подчеркнуть, что стандарт придает очень конкретное значение фразе «неявное преобразование», и она существенно отличается от «контекстного преобразования в bool», которое также имеет очень конкретное значение. Здесь нет никаких отношений типа "есть-а". Я понимаю, что новичкам, вероятно, не нужно сразу знать разницу между неявным / явным / контекстным преобразованием, но лучше подсознательно выучить правильные слова, чем потом ломать старые привычки.
bcrist
22

Проверьте здесь http://www.cplusplus.com/reference/functional/function/operator_bool/

пример

// function::operator bool example
#include <iostream>     // std::cout
#include <functional>   // std::function, std::plus

int main () {
  std::function<int(int,int)> foo,bar;
  foo = std::plus<int>();

  foo.swap(bar);

  std::cout << "foo is " << (foo ? "callable" : "not callable") << ".\n";
  std::cout << "bar is " << (bar ? "callable" : "not callable") << ".\n";

  return 0;
}

Выход

foo не вызывается.

бар вызываемый.

Давид Дрозд
источник
31
Я думаю, что этот ответ был бы более ясным без расширения swap(). Я думал, что результат был обратным, пока не понял это.
cp.engr
0

(Позвольте мне дать четкий ответ.)

Вы можете проверить std::function, пуст ли a с помощью std::function::operator bool.

истина: если объект вызываемый.
false: иначе (объект - пустая функция)

пример

#include <iostream>
#include <functional>

int main ()
{
    std::function<int(int,int)> foo = std::plus<int>();//assigned: not empty
    std::function<int(int,int)> bar;//not assigned: empty

    std::cout << "foo is " << (foo ? "not empty" : "empty") << ".\n";
    std::cout << "bar is " << (bar ? "not empty" : "empty") << ".\n";

    return 0;
}

Выход

foo не пуст.
бар пуст.

zwcloud
источник
2
Ваши строки результатов поменялись местами.
Sophit
@Sophit Ты уверен? ;)
zwcloud
1
В вашем комментарии говорится, что foo не пуст, и вывод не согласуется. Я согласен с вашим комментарием.
Sophit