Можно ли сравнить два функтора на равенство? - PullRequest
6 голосов
/ 15 апреля 2019

Есть ли способ для метода, который получает два функтора в качестве аргументов, чтобы выяснить, указывают ли они на одну и ту же функцию? В частности, имея такую ​​структуру:

struct FSMAction {
    void action1() const { std::cout << "Action1 called." << std::endl; }
    void action2() const { std::cout << "Action2 called." << std::endl; }
    void action3() const { std::cout << "Action3 called." << std::endl; }

private:
    // Maybe some object-specific stuff.
};

И такой метод:

bool actionsEqual(
    const std::function<void(const FSMAction&)>& action1, 
    const std::function<void(const FSMAction&)>& action2)
{
    // Some code.
}

Есть ли "некоторый код", который вернет true только для:

actionsEqual(&FSMAction::action1, &FSMAction::action1)

Но не для:

actionsEqual(&FSMAction::action1, &FSMAction::action2)

Может быть, этот вопрос не имеет никакого смысла (первая подсказка будет о том, что в Интернете, похоже, ничего нет ...). Если да, не могли бы вы дать намек, почему, и если есть способы сделать что-то «подобное»? (По сути, я хотел бы иметь набор обратных вызовов только с «уникальными» элементами в описанном выше смысле.)

Ответы [ 2 ]

3 голосов
/ 15 апреля 2019

Необработанная функция в конце концов является указателем. Вы можете выкопать из std::function с std::function::target, и тогда это просто сравнение void*.

2 голосов
/ 15 апреля 2019

Непосредственное использование std::function::target<T>(), как предлагается в ответе Майкла Чурдакиса, проблематично, поскольку для его использования необходимо знать фактический тип, сохраненный в std::function:

Возвращаемое значение

Указатель на сохраненную функцию, если target_type() == typeid(T), в противном случае - нулевой указатель.

например. используя T = void (A::*)() const, вы ограничиваете себя только использованием void() const функций-членов class FSMAction. На этом этапе std::function начинает быть не лучше простого указателя на функцию-член.


Я предлагаю написать оболочку для std::function, которая реализует == / != с использованием стирания типа. Вот минимальная реализация:

#include <functional>
#include <iostream>
#include <utility>

template <typename T>
class FancyFunction;

template <typename ReturnType, typename ...ParamTypes>
class FancyFunction<ReturnType(ParamTypes...)>
{
    using func_t = std::function<ReturnType(ParamTypes...)>;
    func_t func;
    bool (*eq)(const func_t &, const func_t &) = 0;

  public:
    FancyFunction(decltype(nullptr) = nullptr) {}

    template <typename T>
    FancyFunction(T &&obj)
    {
        func = std::forward<T>(obj);    
        eq = [](const func_t &a, const func_t &b)
        {
            return *a.template target<T>() ==
                   *b.template target<T>();
        };
    }

    explicit operator bool() const
    {
        return bool(func);
    }

    ReturnType operator()(ParamTypes ... params) const
    {
        return func(std::forward<ParamTypes>(params)...);
    }

    bool operator==(const FancyFunction &other) const
    {
        if (func.target_type() != other.func.target_type())
            return 0;

        if (!eq)
            return 1;

        return eq(func, other.func);
    }

    bool operator!=(const FancyFunction &other) const
    {
        return !operator==(other);
    }
};


struct A
{
    void foo() {}
    void bar() {}
};

int main()
{
    FancyFunction<void(A &)> f1(&A::foo), f2(&A::foo), f3(&A::bar);
    std::cout << (f1 == f2) << '\n';
    std::cout << (f1 == f3) << '\n';
}

Попробуй жить

...