
[英]can I use SFINAE to selectively define a member variable in a template class?
[英]Can I use SFINAE to detect template class member functions?
我多次成功使用 SFINAE。 检测一个类是否提供函数不是问题。 我现在的问题似乎与他的问题相反! 我宁愿只检测类的方法,而不是检测派生方法。 这似乎与该方法是模板这一事实有关。
是否可以检测类模板方法? 我试图用一种不应该伤害但没有运气的类型来实例化模板。
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;
}
错误信息:
error: ‘&A::Func’ is not a valid template argument for type ‘void (B::*)(int)’ because it is of type ‘void (A::*)(int)’
请注意,此 SFINAE 非常适用于模板方法,但不适用于派生! 不好的是它不仅检测到错误,而且编译失败。
如何在不使用“样本”类型(这里: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))>
也就是说,使用表达式obj.Func(0)
的类型 ,并将其传递给Sfinae
类模板。
这是带有修复程序的完整代码:
#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
我在此演示中添加了C
类。
在线演示 。 :-)
如果您知道成员函数模板应该具有的确切模板参数,则可以这样做:(请注意,它使用了一些C ++ 11功能,可以用C ++ 03功能代替)
#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;
}
输出:
值A = 1
值B = 0
更新到 C++20(使用概念)为我们提供了这个简洁的 constexpr 解决方案
#include <iostream>
struct A { template<class T> void Func( T ) {}; };
struct B : A {};
struct C {};
template <typename T>
concept CheckForFunc = requires(T t) { t.Func(T()); };
int main(int argc, char* argv[])
{
if constexpr(CheckForFunc<A>) std::cout << "Value A" << std::endl;
if constexpr(CheckForFunc<B>) std::cout << "Value B" << std::endl;
if constexpr(CheckForFunc<C>) std::cout << "Value C" << std::endl;
return 0;
}
输出将是
Value A
Value B
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.