考虑以下代码:
#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 无法使用成员访问表达式形成指向 bar
的 int
重载的指针,有或没有 &
地址- 运营商。 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/