C++ 我可以使用SFINAE检测模板类成员函数吗?
我多次成功地使用了SFINAE。检测类是否提供函数不是问题。我目前的问题似乎与之相反!我宁愿只检测类的方法,而不是检测派生方法。这似乎与该方法是一个模板这一事实有关 是否可以检测类模板方法?我试着用一个不应该有害的类型来实例化模板,但是运气不好C++ 我可以使用SFINAE检测模板类成员函数吗?,c++,templates,sfinae,C++,Templates,Sfinae,我多次成功地使用了SFINAE。检测类是否提供函数不是问题。我目前的问题似乎与之相反!我宁愿只检测类的方法,而不是检测派生方法。这似乎与该方法是一个模板这一事实有关 是否可以检测类模板方法?我试着用一个不应该有害的类型来实例化模板,但是运气不好 struct A { template<class T> void Func( T ) {}; }; struct B :A {}; template< class T > struct CheckForFunc { t
struct A { template<class T> void Func( T ) {}; };
struct B :A {};
template< class T >
struct CheckForFunc
{
typedef char(&YesType)[1];
typedef char(&NoType)[2];
template< class U, void (U::*)( int ) > struct Sfinae;
template< class T2 > static YesType Test( Sfinae<T2,&T2::Func>* );
template< class T2 > static NoType Test( ... );
static const bool value = sizeof(Test<T>(0))==sizeof(YesType);
};
int main(int argc, char* argv[])
{
// gives "1"
std::cout << "Value A=" << CheckForFunc< A >::value << std::endl;
// doesn't compile!
std::cout << "Value B=" << CheckForFunc< B >::value << std::endl;
return 0;
}
请注意,这个SFINAE可以很好地用于模板方法,但不能用于派生!糟糕的是,它不仅检测错误,而且编译失败
如何在不使用“sample”类型(这里是int)的情况下编写SFINAE测试
编辑:抱歉,仅限C++03!LLVM对它很好,也是VS2008,只是不是GCC和QNX(我明天要看的版本)
Edit2:我不知道Coliru的事!很酷,这是最棒的 这个问题与正确解决的类模板无关,而是与成员表达式地址中的一个奇怪怪癖有关。特别是,对于以下类型:
struct base { void foo(); };
struct derived : base {};
表达式&derived::foo
的类型为void(base:*)()
,这可能是直观的,也可能不是直观的
至于检测成员函数模板是否存在的测试,我没有答案。您不能获取模板的地址,但您可能会创建一个虚假的不可访问类型,并尝试使用该类型调用函数。如果函数本身是一个模板,则类可以拥有采用该类型的函数的唯一方法是。您可能希望在未赋值表达式中使用此选项,以避免odr将模板函数用于您的类型。我将使用此修复程序:
Sfinae<T2, decltype(std::declval<T2>().Func(0))>
输出:
Value A=1
Value B=1
Value C=0
我在这个演示中添加了classC
.:-) 如果您知道成员函数模板应该具有的确切模板参数,则可以: (注意,它使用了一些C++11功能,这些功能可以被C++03功能所取代)
#包括
#包括
结构A{template void Func(T){};
结构B:A{};
模板
结构CheckForFunc
{
typedef char(&YesType)[1];
typedef字符(&NoType)[2];
模板静态叶氏试验(
typename std::启用\u如果<
std::是相同的{},
int
>::类型);
模板静态不定型试验(…);
静态常量布尔值=sizeof(测试(0))==sizeof(YesType);
};
int main(int argc,char*argv[])
{
//给出“1”
std::cout为什么不直接使用Test(void(T2::*)(int)=nullptr)
和sizeof(Test())
?我觉得很奇怪!我认为这是一个叮当声中的错误!使用默认参数的想法很有趣。nullptr是C++11吗?实际的问题是Sfinae*
是否会导致替换失败(后一种情况:代码无法编译)对于T2==B
。我倾向于说这是一个替换失败,在这种情况下,clang++是对的,而g++是错的。正如David Rodríguez指出的,它背后的问题是从void(a::*)(int)
转换为void(B:*)(int)在模板参数的上下文中不允许使用
。@KerrekSB:这不会是对T2::Func()的测试,是吗?DyP:是的,似乎是个问题,但是如何使用GCC进行测试?我认为OP希望输出的值B=0
:“与其同时检测派生方法,我更希望只检测类的方法”@DyP:这对我来说没有意义(我也不认为这是可能的),因为基方法一旦继承,就成为派生类的类方法。请参见David Rodríguez的回答。对于非重载函数(和非模板),表达式&B::Func
的类型是void(A::*)(int)
。但是,我不知道OP为什么需要进行检查。@DyP:Correct.Nawaz:subscribe。“表达式&derived::foo的类型为void(base::*)()”这是正确的,但我认为这不是问题所在。OP想要检测一个未继承的成员函数模板(据我所知,OP是这样的)。表达式&A::Func
还没有类型[over.over],但与void(A::*)(int)
和void(B:*)(int)
@DyP:&A::Func
不能与void(B:*)(int)匹配
在模板上,因为它需要从指向base成员的指针转换为指向派生成员的指针,这不是非类型模板参数的有效转换之一。您是对的,我跳过了&a::Func
是一组重载解析候选项的部分,但它们都是void(a::*)(T)
对于某些类型T
(即A
在整个集合中都是固定的。是的,我一开始也这么认为,但在[over.over]中有一条注释:“[注:即,在匹配指向成员函数类型的指针时忽略函数所属的类。-结束注]”@DyP:是的,该引用支持我的观点。重载解析忽略了成员的类型,因此它尝试匹配void(派生::*)(int)
和void(base::*)(int)
通过重载解析被选中,即使base!=派生的
作为保存成员的类型被忽略。此时(重载解析后)根据5.3.1/3,表达式的类型被确定为void(base::*)(int)
而不是void(derived:*)(int)
,即使后者被用于驱动重载解析。请注意,在模板之外,这是完全正确的,因为存在从void(base:*)(int)的隐式转换
到void(派生::*)(int)
,但在匹配非类型模板参数时,该转换不是允许的转换之一。
#include <iostream>
#include <utility>
struct A { template<class T> void Func( T ) {}; };
struct B : A {};
struct C {};
template< class T >
struct CheckForFunc
{
typedef char(&YesType)[1];
typedef char(&NoType)[2];
template< class, class > struct Sfinae;
template< class T2 > static YesType Test( Sfinae<T2, decltype(std::declval<T2>().Func(0))> * );
template< class T2 > static NoType Test( ... );
static const bool value = sizeof(Test<T>(0))==sizeof(YesType);
};
int main(int argc, char* argv[])
{
std::cout << "Value A=" << CheckForFunc< A >::value << std::endl;
std::cout << "Value B=" << CheckForFunc< B >::value << std::endl;
std::cout << "Value C=" << CheckForFunc< C >::value << std::endl;
return 0;
}
Value A=1
Value B=1
Value C=0
#include <iostream>
#include <type_traits>
struct A { template<class T> void Func( T ) {} };
struct B :A {};
template< class T >
struct CheckForFunc
{
typedef char(&YesType)[1];
typedef char(&NoType)[2];
template< class T2 > static YesType Test(
typename std::enable_if<
std::is_same<void (T::*)(int), decltype(&T2::template Func<int>)>{},
int
>::type );
template< class T2 > static NoType Test( ... );
static const bool value = sizeof(Test<T>(0))==sizeof(YesType);
};
int main(int argc, char* argv[])
{
// gives "1"
std::cout << "Value A=" << CheckForFunc< A >::value << std::endl;
// doesn't compile!
std::cout << "Value B=" << CheckForFunc< B >::value << std::endl;
return 0;
}