5

我想知道是否可以扩展 SFINAE 方法来检测类是否具有某个成员函数(如此处所述:

“C++ 中是否有一种技术可以知道一个类是否具有给定签名的成员函数?” 检查一个类是否具有给定签名的成员函数

) 支持模板化成员函数?例如,能够检测以下类中的函数 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)。

如果我注释掉 的...重载test以强制编译器选择另一个,我会在 g++ 中得到以下编译器错误:

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

其中第 32 行是该enum { value = sizeof( test< _class >( 0 ) ) == sizeof( big ) };行。不幸的是,这似乎并不能帮助我诊断问题:(。MSVC++ 给出了一个类似的不伦不类的错误:

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 >()然后我没有收到编译错误&_t::template foo< 5 >。这表明只有在 SFINAE 上下文中从本身是模板参数的类型中获取模板化成员函数的地址时才会出现问题。啊!

4

1 回答 1

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>;
}
于 2010-01-25T15:26:47.790 回答