我可以使用SFINAE来检测模板类成员函数吗?

时间:2013-08-06 16:53:03

标签: c++ templates sfinae

我多次成功使用SFINAE。检测类是否提供函数不是问题。我目前的问题似乎与his problem相反!而不是检测派生方法,我宁愿检测类的方法。这似乎与该方法是模板的事实有关。

是否可以检测类模板方法?我试图用一种不应该伤害的类型来实例化模板,但是没有运气。

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与模板方法的效果非常好,而不是派生!不好的是它不仅检测错误,而且编译失败。

如何在不使用'sample'类型的情况下编写SFINAE测试(此处:int)?

编辑:抱歉,仅限C ++ 03! LLVM也很好用,VS2008也好,不是GCC和QNX(我明天要看的版本)。

Edit2:不知道Coliru!非常酷,这是error

3 个答案:

答案 0 :(得分:3)

该问题与正确解析的类模板无关,而与成员地址表达式中的奇怪怪癖有关。特别是,类型:

struct base { void foo(); };
struct derived : base {};

表达式&derived::foo的类型为void (base::*)(),可能是不直观的。

在检测成员函数模板存在的测试中,我没有答案。您不能获取模板的地址,但您可能会创建一个假的不可访问类型并尝试使用该类型调用该函数。如果函数本身是模板,那么类可以使用该类型的函数的唯一方法是。您可能希望在未评估的表达式中使用此内容,以避免使用您的类型 odr-using 模板函数。

答案 1 :(得分:2)

我会使用此修复程序:

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

Online Demo。 : - )

答案 2 :(得分:0)

如果您知道成员函数模板应具有的确切模板参数,则可以: (注意它使用了一些可以被C ++ 03特性替换的C ++ 11特性)

#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