为什么(成员)函数指针在Visual C ++中表现得如此奇怪?

时间:2013-01-05 20:55:40

标签: c++ visual-c++ linker

我有一个非常奇怪的问题,我已经减少到以下测试用例:

#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将被称为......

启用优化时,这真的很奇怪。发生了什么事?

3 个答案:

答案 0 :(得分:27)

这是Visual C ++所指的相同的COMDAT折叠(ICF)的副产品。它将相同的函数合并到一个实例中。您可以通过将以下开关添加到链接器命令行来禁用它:/OPT:NOICF(来自Visual Studio UI,它位于 Properties-&gt; Linker-&gt; Optimization-&gt; Enable COMDAT Folding )

您可以在MSDN文章中找到详细信息:/OPT (Optimizations)

交换机是链路级交换机,这意味着您无法仅为特定模块或特定代码区域(例如__pragma( optimize() )启用它,可用于编译器阶段优化)。

但是,一般情况下,依靠函数指针或文字字符串指针(const char*)来测试唯一性被认为是不好的做法。几乎所有的C / C ++编译器都广泛实现了字符串折叠。功能折叠目前仅在Visual C ++上可用,但增加了对模板&lt;&gt;的广泛使用。元编程增加了将此功能添加到gcc和clang工具链的请求。

编辑:从binutils 2.19开始,附带的黄金链接器据称也支持ICF,但我无法在本地Ubuntu 12.10安装上验证它。

答案 1 :(得分:18)

事实证明,Visual C ++的链接器可以将具有相同定义的函数合并为一个 根据C ++,这是否合法,我不知道;它会影响可观察的行为,所以它看起来像是一个bug。其他有更多信息的人可能想要加入其中。

答案 2 :(得分:7)

C ++ 11 5.3.1描述了&的作用;在这个例子中,它为你提供了一个指向所讨论的成员函数的指针,并且该段落不要求该指针必须是唯一的。

但是,5.10 / 1表示关于==

  

同一类型的两个指针比较相等,当且仅当它们都为空时,都指向相同的函数,或者两者都代表相同的地址。

然后问题变成......是test1test2“相同的功能”?

虽然优化器已将它们合并为单个定义,但可以说这两个名称标识了两个函数,因此,这似乎是一个实现错误

(但请注意,VS团队并不关心并认为它“足够有效”以保证优化的好处。或者they don't realise that it's invalid。)

我坚持使用字符串作为函数指针的“句柄”。