c++ - msvc visual c++ 从静态成员函数不正确地形成绑定(bind)成员函数表达式

标签 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 编译器都可以。

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

但是,错误消息的措辞令人不安,人们不得不怀疑,对于任何其他替代方案,是否错误地形成了绑定(bind)成员函数表达式。

对这件事有什么见解吗?

最佳答案

这是标准的一个不错的、黑暗的角落,供语言律师享用。

[expr.ref] ¶4.3

If E2 is a (possibly overloaded) member function, function overload resolution is used to determine whether E1.E2 refers to a static or a non-static member function.

  • If it refers to a static member function and the type of E2 is "function of parameter-type-list returning T", then E1.E2 is an lvalue; the expression designates the static member function. The type of E1.E2 is the same type as that of E2, namely "function of parameter-type-list returning T".

  • Otherwise, if E1.E2 refers to a non-static member function and the type of E2 is "function of parameter-type-list cv ref-qualifieropt returning T", then E1.E2 is a prvalue. The expression designates a non-static member function. The expression can be used only as the left-hand operand of a member function call. The type of E1.E2 is "function of parameter-type-list cv returning T".

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

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

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

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

这是一个相关的例子:

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 无法使用成员访问表达式形成指向 barint 重载的指针,有或没有 & 地址- 运营商。 MSVC 不一致,因为它在没有 & 的情况下成功,但没有它。但由于上述原因,很难说哪个是符合的。 GCC 在所有情况下都非常乐意让成员访问表达式指定一个未解析的重载,然后由于初始化的目标类型([over.over] ¶1)而被解析。

当 SFINAE 参与进来时,它变得更加有趣:

[temp.over] ¶1

...If, for a given function template, argument deduction fails or the synthesized function template specialization would be ill-formed, no such function is added to the set of candidate functions for that template...

因此,为了确定成员函数的“静态性”,某些编译器可能会将 SFINAE 失败排除在用于重载解析的候选函数集中。但同样,这里很难说什么是符合的。这也许可以解释为什么 Clang 接受了原始问题中的示例,而不是我的 bar 示例。我感觉实现者只是在尽力填补这里的空白。

关于c++ - msvc visual c++ 从静态成员函数不正确地形成绑定(bind)成员函数表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41411009/

相关文章:

python - swig 将元组列表传递给 C++ 函数

c++ - 根据 3 个数字中最小的那个进行分支

c++ - MSVC constexpr 函数 'xyz' 无法生成常量表达式

visual-studio-2015 - ASP.NET 5 beta 7 的共享类库引用

visual-studio - Visual Studio 2015-按住Shift + Enter时括号不会自动格式化

c++ - 堆栈与堆 C++

c++ - 如何编写垂直右侧的 IE 资源管理器栏

c++ - 如何在 Visual C++ 6 程序中正确修改 .def 文件?

c++ - 动态对齐问题

c++ 模板在 vs express 2015 中看不到