2

我多次成功使用 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: ‘&amp;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!非常酷,这是错误

4

4 回答 4

3

该问题与已正确解决的类模板无关,而是与成员地址表达式中的一个奇怪的怪癖有关。特别是,使用以下类型:

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

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

作为检测成员函数模板是否存在的测试,我没有答案。您不能获取模板的地址,但您可能会创建一个虚假的不可访问类型并尝试使用该类型调用函数。类可以具有采用该类型的函数的唯一方法是函数本身是模板。您可能希望在未计算的表达式中使用它,以避免在您的类型中使用模板函数。

于 2013-08-06T17:05:57.287 回答
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在这个演示中添加了类。

在线演示。:-)

于 2013-08-06T17:06:49.823 回答
1

更新到 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
于 2022-01-25T21:51:36.633 回答
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

于 2013-08-06T17:49:15.500 回答