C++ 将成员函数转换为指向成员函数的指针
Clang、GCC、MSVC对成员函数的转换有不同的看法。 谁是对的C++ 将成员函数转换为指向成员函数的指针,c++,templates,C++,Templates,Clang、GCC、MSVC对成员函数的转换有不同的看法。 谁是对的 模板 结构a { 模板 void va(Args…{} 模板 空位x(x){} 空y(int){} }; 结构b:a { void testva() { 使用F=void(a::*)(); F=(F)&a::va;//gcc:error,msvc:error,clang:ok } void testx() { 使用F=void(a::*)(); F=(F)&a::x;//gcc:error,msvc:ok,clang:ok
模板
结构a
{
模板
void va(Args…{}
模板
空位x(x){}
空y(int){}
};
结构b:a
{
void testva()
{
使用F=void(a::*)();
F=(F)&a::va;//gcc:error,msvc:error,clang:ok
}
void testx()
{
使用F=void(a::*)();
F=(F)&a::x;//gcc:error,msvc:ok,clang:ok
}
void testy()
{
使用F=void(a::*)();
F=(F)&a::y;//gcc:ok,msvc:ok,clang:ok
}
};
clang是对的
如果T1
类型的“指向X
成员的指针”和T2
类型的“指向Y
成员的指针”都是函数类型或对象类型,则可以将类型为“指向T2
类型的成员的指针”的prvalue显式转换为不同类型的prvalue。空成员指针值将转换为目标类型的空成员指针值。此转换的结果未指定,以下情况除外:
- 将“指向成员函数的指针”类型的prvalue转换为指向成员函数类型的不同指针并返回其原始类型将生成指向成员值的原始指针
- 将
类型的“指向T1
的数据成员的指针”类型的PR值转换为X
类型的“指向T2
的数据成员的指针”(其中Y
的对齐要求并不比T2
的对齐要求更严格)返回到其原始类型将生成指向成员值的原始指针T1
&a::va
等。类型为“指向a
成员的a
类型为void(int)
”的指针的prvalue并将其转换(不调用未指定值的结果函数指针)是合法的。testx
和testy
格式正确,因此gcc对testx
的理解是错误的。但是该标准在testva
方面有些模糊
从最简单的开始,在testy
中,表达式&a::y
命名了一个未重载的非模板函数,因此它的类型为void(a::*)(int)
,无需进一步分析。从任何指向成员函数的指针到任何其他指向成员函数的指针的转换都是格式良好的重新解释类型转换
,除非转换回原始类型,否则会产生未指定的结果,C样式转换可以做重新解释类型转换
可以做的事情
对于模板函数,我们有:
在某些上下文中,不带参数的重载函数名的使用解析为重载集中特定函数的函数、函数指针或成员函数指针。函数模板名称被认为是在这样的上下文中命名一组重载函数。如果F
(可能应用函数指针转换后)与FT
相同,则为上下文中所需目标类型的函数类型FT
选择类型为F
的函数。目标可以是
- 显式类型转换([expr.type.conv]、[expr.static.cast]、[expr.cast])
a::x
进行模板参数推断,将其与目标类型void(a::*)()
匹配。但是没有专门化可以提供精确匹配,因为它们都有一个参数,而不是零,因此推断失败。但是根据注释,还有[temp.arg.explicit](C++17中的第3段,):
可以从显式模板参数列表中忽略可以从默认模板参数推导或获取的后续模板参数。后续模板参数包([temp.variadic])如果指定了模板参数列表,且该列表与任何默认模板参数一起标识单个函数模板专用化,则模板id为n函数模板专用化的左值
在testx
中,模板ida::x
标识单个函数模板专门化。因此它命名该专门化,并且C样式转换同样有效,结果未指定
那么在testva
中,a::va
是否识别一个专门化?当然可以通过以下方式使用该表达式命名不同的专门化:
模板参数推断可以扩展与模板参数包对应的模板参数序列,即使该序列包含显式指定的模板参数
除了上面说的“模板参数推断”。这里涉及的模板参数推断失败了,因为它需要与目标类型void(a::*)()
进行不可能的匹配。因此,没有任何东西真正解释a::va
是否识别单个专门化,因为没有其他获取
template<typename T>
struct a
{
template <typename... Args>
void va(Args...) {}
template <typename X>
void x(X) {}
void y(int) {}
};
struct b : a<b>
{
void testva()
{
using F = void (a<b>::*)();
F f = (F)&a<b>::va<int>; // gcc: error, msvc: error, clang: ok
}
void testx()
{
using F = void (a<b>::*)();
F f = (F)&a<b>::x<int>;// gcc: error, msvc: ok, clang: ok
}
void testy()
{
using F = void (a<b>::*)();
F f = (F)& a<b>::y; // gcc: ok, msvc: ok, clang: ok
}
};