29

私は本当に奇妙な問題を抱えていましたが、それを次のテストケースに減らしました。

#include <iostream>
#include <map>
#include <string>

struct Test
{
    std::map<std::string, void (Test::*)()> m;
    Test()
    {
        this->m["test1"] = &Test::test1;
        this->m["test2"] = &Test::test2;
    }
    void test1() { }
    void test2() { }
    void dispatch(std::string s)
    {
        if (this->m.at(s) == &Test::test1)
        { std::cout << "test1 will be called..." << std::endl; }
        else if (this->m.at(s) == &Test::test2)
        { std::cout << "test2 will be called..." << std::endl; }
        (this->*this->m.at(s))();
    }
};

int main()
{
    Test t;
    t.dispatch("test1");
    t.dispatch("test2");
}

出力します

test1が呼び出されます...
test1が呼び出されます...

最適化が有効になっているとき、これは本当に奇妙です。どうしたの?

4

3 に答える 3

27

これは、Visual C++ が同一 COMDAT フォールディング(ICF) と呼ぶものの副産物です。同一の機能を単一のインスタンスにマージします。リンカー コマンドラインに次のスイッチを追加することで無効にできます: /OPT:NOICF (Visual Studio UI から、[プロパティ] -> [リンカー] -> [最適化] -> [COMDAT フォールディングを有効にする] の下にあります)

詳細については、次の MSDN 記事を参照してください: /OPT (最適化)

__pragma( optimize() )このスイッチはリンカー ステージのスイッチです。つまり、特定のモジュールまたはコードの特定の領域 (コンパイラ ステージの最適化に使用できるものなど) に対してのみ有効にすることはできません。

const char*ただし、一般に、一意性をテストするために関数ポインターまたはリテラル文字列ポインター ( ) に依存することは、不適切な方法と見なされます。文字列の折りたたみは、ほぼすべての C/C++ コンパイラで広く実装されています。関数の折りたたみは、現時点では Visual C++ でのみ利用できますが、template<> メタプログラミングの普及により、この機能を gcc および clang ツールチェーンに追加する要求が増加しています。

編集: binutils 2.19 以降、含まれているゴールド リンカーは ICF もサポートしていると思われますが、ローカルの Ubuntu 12.10 インストールでは確認できませんでした。

于 2013-01-06T17:22:32.293 に答える
18

Visual C ++のリンカは、同じ定義を持つ関数を1つにマージできることがわかりました。
それがC++によると合法であるかどうかにかかわらず、私にはわかりません。観察可能な動作に影響を与えるため、私にはバグのように見えます。しかし、より多くの情報を持っている他の誰かがそれについてチャイムをしたいと思うかもしれません。

于 2013-01-05T20:55:39.633 に答える
7

C++11 5.3.1 では、何を行うかについて説明して&います。この例では、問題のメンバー関数へのポインターが提供されます。この文章では、このポインターが一意でなければならないという要件はありません。

ただし、5.10/1 は次のように述べてい==ます。

同じ型の 2 つのポインターは、両方が null であるか、両方が同じ関数を指しているか、または両方が同じアドレスを表している場合にのみ、等しいと見なされます。

質問は次のようになります...test1test2「同じ機能」ですか?

オプティマイザはそれらを 1 つの定義にまとめましたが、おそらく 2 つの名前は 2 つの関数を識別しているため、これは実装のバグのように思われます

(ただし、VS チームは気にせず、最適化の利点を正当化するのに「十分に有効」であると考えていることに注意してください。そうしないと、それが無効であることを認識していません。)

関数ポインターの「ハンドル」として文字列を使用することに固執します。

于 2013-01-05T21:19:22.607 に答える