我想知道是否可以扩展SFINAE方法以检测某个类是否具有某个成员函数(如下所述:
“C ++中是否有技术可以知道某个类是否具有给定签名的成员函数?” Check if a class has a member function of a given signature
)支持模板化成员函数?例如。能够检测以下类中的函数foo:
struct some_class {
template < int _n > void foo() { }
};
我认为可以为foo的特定实例化执行此操作(例如,检查void foo< 5 >()
是否为成员),如下所示:
template < typename _class, int _n >
class foo_int_checker {
template < typename _t, void (_t::*)() >
struct sfinae { };
template < typename _t >
static big
test( sfinae< _t, &_t::foo< _n > > * );
template < typename _t >
static small
test( ... );
public:
enum { value = sizeof( test< _class >( 0 ) ) == sizeof( big ) };
};
然后执行foo_int_checker< some_class, 5 >::value
检查some_class
是否有成员void foo< 5 >()
。但是在MSVC ++ 2008上,这总是返回false
,而g ++在行test( sfinae< _t, &_t::foo< _n > > );
test.cpp:24: error: missing `>' to terminate the template argument list
test.cpp:24: error: template argument 2 is invalid
test.cpp:24: error: expected unqualified-id before '<' token
test.cpp:24: error: expected `,' or `...' before '<' token
test.cpp:24: error: ISO C++ forbids declaration of `parameter' with no type
两者似乎都失败了,因为我试图从一个本身就是模板参数的类型中获取模板函数实例化的地址。是否有人知道这是否可能,或者由于某种原因它是否被标准禁止?
编辑:似乎我错过了::template
语法来让g ++正确编译上面的代码。如果我将我获得函数地址的位更改为&_t::template foo< _n >
,则程序将编译,但我得到与MSVC ++相同的行为(value
始终设置为false
)。< / p>
如果我注释掉...
的{{1}}重载以强制编译器选择另一个,我在g ++中得到以下编译器错误:
test
其中第32行是test.cpp: In instantiation of `foo_int_checker<A, 5>':
test.cpp:40: instantiated from here
test.cpp:32: error: invalid use of undefined type `class foo_int_checker<A, 5>'
test.cpp:17: error: declaration of `class foo_int_checker<A, 5>'
test.cpp:32: error: enumerator value for `value' not integer constant
行。不幸的是,这似乎没有帮助我诊断问题:(。MSVC ++给出了类似的不明白错误:
enum { value = sizeof( test< _class >( 0 ) ) == sizeof( big ) };
在同一条线上。
奇怪的是,如果我从特定的类而不是模板参数获取地址(即而不是error C2770: invalid explicit template argument(s) for 'clarity::meta::big checker<_checked_type>::test(checker<_checked_type>::sfinae<_t,&_t::template foo<5>> *)'
我&_t::template foo< _n >
),那么我得到了正确的结果,但接着是我的检查员类仅限于检查函数的单个类(&some_class::template foo< _n >
)。另外,如果我执行以下操作:
some_class
并调用template < typename _t, void (_t::*_f)() >
void
f0() { }
template < typename _t >
void
f1() {
f0< _t, &_t::template foo< 5 > >();
}
然后我就不会在f1< some_class >()
上收到编译错误。这表明只有在SFINAE上下文中从类型本身作为模板参数获取模板化成员函数的地址时才会出现问题。哎呀!
答案 0 :(得分:1)
在Boost.MPL中已经实现了类似的东西,它被称为“BOOST_MPL_HAS_XXX_TRAIT_DEF”。参见:
http://www.boost.org/doc/libs/1_41_0/libs/mpl/doc/refmanual/has-xxx-trait-def.html
它可以检测该类是否具有给定的命名类型。
另外,对于您的特定情况,不要将函数指针作为参数传递(void(_t :: *)()),而是尝试在方法体中使用它,即类似:
template < typename _t >
static big test( sfinae<_t> )
{
&_t::foo<_n>;
}