msvc visual c ++不正确地形成静态成员函数的绑定成员函数表达式

时间:2016-12-31 19:02:52

标签: c++ visual-c++ visual-studio-2015 compiler-bug

请考虑以下代码:

#include <type_traits>

struct A {
    template<typename T, std::enable_if_t<sizeof(T)<=sizeof(int), int> = 0>
    static void fun() {}
    template<typename T, std::enable_if_t<(sizeof(T)>sizeof(int)), int> = 0>
    static void fun() {}
    void test() {
        using fun_t = void(*)();
        fun_t ff[] = {
            fun<int>,
            &fun<int>,
            A::fun<int>,
            &A::fun<int>,
            this->fun<int>,
            &this->fun<int>, //error C2276: '&': illegal operation on bound member function expression
        };
    }
};

msvc 2015update3和2017rc生成错误C2276,这没有任何意义。 gcc,clang和intel编译器都没问题。

解决方法很简单:使用上面的任何其他表达式都应该是等价的。

但是,错误消息的措辞是令人不安的,并且不得不怀疑,如果为任何其他替代方案错误地形成了绑定成员函数表达式。

对此事有何见解?

1 个答案:

答案 0 :(得分:1)

对于语言律师来说,这是一个很好的黑暗角落。

  

[expr.ref]¶4.3

     

如果E2是(可能是重载的)成员函数,函数重载决策用于确定E1.E2是指静态成员函数还是非静态成员函数

     
      
  • 如果它引用静态成员函数并且E2的类型是“参数类型列表返回T的函数”,则E1.E2是左值;表达式指定静态成员函数。 E1.E2的类型与E2的类型相同,即“参数类型列表返回T的函数”。

  •   
  • 否则,如果E1.E2引用非静态成员函数,并且E2的类型是“参数类型列表 cv ref-qualifier opt <的函数/ em>返回T“,然后E1.E2是prvalue。表达式指定非静态成员函数。 表达式只能用作成员函数调用的左侧操作数。 E1.E2的类型是“参数类型列表cv返回T的函数”。

  •   

不言而喻,只有指向 static 成员函数的指针才能由成员访问表达式(即点或箭头运算符)形成。但需要注意的是,重载决策决定了成员函数的“静态”。所以问题是,如何在没有参数列表的情况下执行重载决策?编译器的解释似乎有所不同。

有些编译器可能会认识到,因为表达式用于形成指向函数的指针,函数必须是静态成员函数,否则程序生成错误,因此排除非静态成员函数来自候选函数集。

其他编译器可能会认识到因为所有重载都是静态的,所以重载解析不可能选择非静态成员函数,因此完全跳过该步骤。

然而,其他编译器可能完全放弃,面对在没有任何参数列表的情况下应用重载决策的指令。

这是一个相关的例子:

struct A {
    static void foo(int) {}
    static void bar(int) {}
    static void bar(double) {}
};

void test() {
    A a;
    void(*f)(int) =  a.foo; // gcc:OK, msvc:OK,    clang:OK
    void(*g)(int) =  a.bar; // gcc:OK, msvc:OK,    clang:ERROR
    void(*h)(int) = &a.bar; // gcc:OK, msvc:ERROR, clang:ERROR
}

这里Clang无法使用成员访问表达式形成指向int bar重载的指针,无论是否有& address-of运算符。 MSVC不一致,因为它在没有&但没有bar的情况下成功。但由于上述原因,很难说哪个符合要求。在所有情况下,GCC都非常高兴让成员访问表达式指定一个未解析的重载,然后由于初始化的目标类型([over.over]¶1)而解决该重载。

当SFINAE参与时,它会变得更有趣:

  

[temp.over]¶1

     

...如果对于给定的函数模板,参数推导失败或者合成的函数模板特化将是格式错误的,则不会将该函数添加到该模板的候选函数集中...

因此,为了确定成员函数的“静态性”,一些编译器可以从用于重载解析的候选函数集中排除SFINAE失败。但同样,很难说这里符合什么。这可以解释为什么Clang接受原始问题中的示例,而不是我的SELECT to_char((col1 - col0), 'HH24 hrs MI "minutes" SS "seconds"') FROM T1; 示例。我觉得实施者正在尽力填补这里的空白。