比较std :: functions是否相等?

时间:2013-12-30 04:43:23

标签: c++ c++11 function-pointers std-function

如何将两个C ++ 11 std::functionoperator==进行比较,如果所有true都引用相同的函数指针,则返回function?< / p>

7 个答案:

答案 0 :(得分:15)

operator== std :: function std :: function 与空指针进行比较,据我所知,标准不提供任何详细说明原因。

虽然这个提升常见问题解答条目Why can't I compare boost::function objects with operator== or operator!=?提供了一个基本原理,据我所知,它也适用于std::function。引用常见问题解答:

  

boost :: function对象之间的比较不能“很好”地实现,因此不会实现。 [...]

然后,它概述了类似于Preet的请求解决方案,接着说:

  

当f和g存储的函数对象的类型没有运算符时会出现问题== [...]

并解释为什么必须在赋值运算符或构造函数中处理它,然后继续说:

  

所有这些问题都转化为boost :: function构造函数或赋值运算符中的失败,即使用户从不调用operator ==。我们不能对用户这样做。

更新

Accessing the target of a tr1::function object中找到了一个标准基本原理,这个标准基本上很古老,但与提升常见问题解答一致并说:

  

operator ==对于C ++语言中的tr1 :: function是无法实现的,因为在没有用户帮助的情况下,我们没有可靠的方法来检测给定类型T是否为Equality Comparable。

答案 1 :(得分:13)

您实际上可以使用.target

template<typename T, typename... U>
size_t getAddress(std::function<T(U...)> f) {
    typedef T(fnType)(U...);
    fnType ** fnPointer = f.template target<fnType*>();
    return (size_t) *fnPointer;
}

if (getAddress(f) == getAddress(g)) {...}

(参考:C++ trying to get function address from a std::function

答案 2 :(得分:6)

您可以首先比较ab,然后比较它们的.target_type(),如果这些目标类型ID相同,那么您可以比较它们的.target()指针。您可以使用不匹配的目标类型作为早期错误。

答案 3 :(得分:2)

如果std::function<T(U...)> f是成员函数,fnPointer将为空。

答案 4 :(得分:2)

请注意,函数的相等性(决定两个函数是否总是具有相同的可观察行为)是lambda演算中不可判定的问题(这就是许多编程语言禁止比较函数的原因)。

因此,即使==测试编译,它最多只会测试代码是否相同(具有相同的地址),而不是比较的函数具有相同的行为。

答案 5 :(得分:1)

好吧,如果您不害怕黑客攻击,则可以执行以下操作:

// Simple function means no std::bind was used
bool IsSimpleFunction(std::function<void(Args...)> function)
{
    typedef void(functionType)(Args...);
    functionType** functionPointer = function.template target<functionType*>();
    return functionPointer != NULL;
}

bool AreEqual(std::function<void(Args...)> left, std::function<void(Args...)> right)
{
    const int size = sizeof(std::function<void(Args...)>);
    std::byte leftArray[size] = { {(std::byte)0} };
    std::byte rightArray[size] = { {(std::byte)0} };
    std::byte* leftByte = (std::byte*) new (&leftArray) std::function<void(Args...)>(left);
    std::byte* rightByte = (std::byte*) new (&rightArray) std::function<void(Args...)>(right);

    // PrintFunctionsBytes(leftByte, rightByte, size);

    // Here the HACK starts
    // By resetting certain values we are able to compare functions correctly
    // When values are reset it has the same effect as when these values are ignored
    bool isSimpleFunction = IsSimpleFunction(left);
    if (!isSimpleFunction)
    {
        ResetAt(leftArray, rightArray, 16);
    }
    ResetAt(leftArray, rightArray, 56);
    ResetAt(leftArray, rightArray, 57);
    // Here the HACK ends

    for (int i = 0; i < size; i++, leftByte++, rightByte++)
    {
        if (*leftByte != *rightByte)
        {
            return false;
        }
    }
    return true;
}

void ResetAt(std::byte* leftArray, std::byte* rightArray, int i)
{
    leftArray[i] = (std::byte)0;
    rightArray[i] = (std::byte)0;
}

// Only for debug
void PrintFunctionsBytes(std::byte* leftFirstByte, std::byte* rightFirstByte, unsigned long long size)
{
    std::vector<std::byte> leftVector(leftFirstByte, leftFirstByte + size);
    std::vector<std::byte> rightVector(rightFirstByte, rightFirstByte + size);
    std::cout << "Left: ";
    for (int i = 0; i < size; i++)
    {
        std::cout << i << ':' << (int)leftVector[i] << std::endl;
    }
    std::cout << "Right: ";
    for (int i = 0; i < size; i++)
    {
        std::cout << i << ':' << (int)rightVector[i] << std::endl;
    }
}

这已在MSVC和64位发行版配置中进行了测试。它适用于简单的函数,也可以将std :: bind转换为std :: function。

如果您使用不同的编译器或构建配置,则必须针对您的环境调整被忽略的字节。

完整示例如下:https://github.com/linksplatform/Delegates

答案 6 :(得分:0)

比较两个shared_ptr怎么样?

using MessageFilter = std::function<void(const int msgID)>;

static void onMessageReceived(const int msgID)
{
    std::cout << "msg id => " << msgID << std::endl;
}

static void someFunc()
{
    auto filter = std::make_shared<MessageFilter>(&onMessageReceived);

    if (filter && *filter)
    {
        (*filter)(1234);
    }
}

如您所见,'filter'是shared_ptr,因此很容易与另一个进行比较。