web-dev-qa-db-ja.com

std :: functionsの同等性の比較?

2つのC++ 11を比較するにはどうすればよいですかstd::functionsとoperator==、およびtruesの両方が同じ関数ポインターを参照している場合はfunctionを返しますか?

35
JesseTG

operator == forstd :: functionstd :: functionnullポインタを使用しますが、標準が理由について詳細を提供していないことがわかる限り、.

ただし、このブーストFAQエントリ、- boost :: functionオブジェクトをoperator ==またはoperator!=?と比較できない理由 は、根拠を提供し、 std :: function にも適用できると私は言うことができます。

Boost :: functionオブジェクトの比較は「うまく」実装できないため、実装されません。 [...]

次に、要求されたPreetと同様のソリューションの概要を示し、続けて次のように述べます。

この問題は、fとgの両方に保存されている関数オブジェクトのタイプにoperator == [...]がない場合に発生します。

そして、なぜこれを代入演算子またはコンストラクタのいずれかで処理する必要があるのか​​を説明し、次に続けます。

これらの問題はすべて、ユーザーがoperator ==を呼び出さない場合でも、boost :: functionコンストラクターまたは代入演算子の失敗につながります。ユーザーにそれを行うことはできません。

更新

tr1 :: functionオブジェクトのターゲットへのアクセス に標準の根拠が見つかりました。これはかなり古いですが、ブーストと一致していますFAQと言い、

operator ==は、C++言語内のtr1 :: functionには実装できません。これは、ユーザーの支援なしに、特定の型Tが等価比較可能かどうかを検出する信頼できる方法がないためです。

19
Shafik Yaghmour

実際に.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++がstd :: functionから関数アドレスを取得しようとしている

14
P i

最初にabを比較してみて、それらの .target_type() を比較し、これらのターゲットタイプIDが同じである場合、それらを比較することができます .target() ポインタ。不一致のターゲットタイプをアーリーアウトfalseとして使用できます。

5
Preet Kukreti

関数の等価性(2つの関数の観測可能な動作が常に同じかどうかを判断すること)は、ラムダ計算では決定不能な問題であることに注意してください(そのため、多くのプログラミング言語では関数の比較が禁止されています)。

したがって、==テストがコンパイルされます。比較される関数の動作が同じであることをテストするのではなく、コードが同一(同じアドレスを持っている)であることをテストするだけです。

std::function<T(U...)> fがメンバー関数の場合、fnPointerはnullになります。

2
yksten

まあ、ハッキングを恐れていない場合は、次のようなことができます。

// 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

1
Konard