msvc visual c++从静态成员函数中不正确地形成绑定成员函数表达式
Posted
技术标签:
【中文标题】msvc visual c++从静态成员函数中不正确地形成绑定成员函数表达式【英文标题】:msvc visual c++ incorrect formation of bound member function expression from static member function 【发布时间】:2016-12-31 19:02:52 【问题描述】:考虑以下代码:
#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】:对于语言律师来说,这是标准中一个不错的黑暗角落。
[expr.ref] ¶4.3
如果 E2 是(可能重载的)成员函数,函数重载决议用于确定 E1.E2 是指静态成员函数还是非静态成员函数。
如果引用静态成员函数,并且E2的类型是“参数类型列表返回T的函数”,那么E1.E2是左值;表达式指定静态成员函数。 E1.E2的类型与E2的类型相同,即“参数类型列表返回T的函数”。
否则,如果 E1.E2 引用一个非静态成员函数并且 E2 的类型是“参数类型列表的函数 cv ref-qualifieropt 返回 T",则 E1.E2 是纯右值。该表达式指定一个非静态成员函数。 表达式只能用作成员函数调用的左侧操作数。 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
的指针,无论是否使用 &
地址操作符。 MSVC 的不一致之处在于它在没有&
的情况下成功,但没有它。但是由于上述原因,很难说哪个是一致的。 GCC 在所有情况下都非常乐意让成员访问表达式指定一个未解决的重载,然后由于初始化的目标类型([over.over] ¶1)而解决该重载。
SFINAE 参与进来会更有趣:
[temp.over] ¶1
...如果对于给定的函数模板,参数推导失败或合成的函数模板特化格式不正确,则不会将此类函数添加到该模板的候选函数集中...
因此,为了确定成员函数的“静态性”,一些编译器可能会从候选函数集中排除 SFINAE 失败以进行重载解析。但同样,这里很难说什么是一致的。这可以解释为什么 Clang 接受原始问题中的示例,而不是我的 bar
示例。我感觉实施者只是在尽最大努力填补这里的空白。
【讨论】:
以上是关于msvc visual c++从静态成员函数中不正确地形成绑定成员函数表达式的主要内容,如果未能解决你的问题,请参考以下文章
C++ 多个版本的默认特殊成员函数 -- MSVC 2015 中的错误
从 C (Visual Studio) 调用 C++ 类成员函数
静态代码分析器:非托管C ++ Visual Studio 2008