【问题标题】:SFINAE approach comparisonSFINAE 方法比较
【发布时间】:2010-12-05 11:35:21
【问题描述】:

以下代码显示了一个 SFINAE 实现,用于在编译时检查一个类型(基本上是一个类)是否包含成员函数 member_func

#define CHECKER(func_name,class_name) sizeof(class_name<T>::template func_name<T>(0)) == 1
#include <iostream>
struct A
{
    void member_func();
};
struct B {};
template<typename T>struct Check_If_T_Is_Class_Type
{
    template<typename C> static char func (char C::*p);
    template<typename C> static long func (...);
    enum{val = CHECKER(func,Check_If_T_Is_Class_Type)};
};

//APPROACH 1
template <typename T>struct TypeHasMemberFunc
{
    template <typename C, C> struct TypeCheck;
    template <typename C> struct Prototype_Holder {typedef void (C::*fptr)();};
    template <typename C> static char func(TypeCheck
                                           <
                                              typename Prototype_Holder<C>::fptr,
                                              &C::member_func
                                           >*);
    template <typename C> static long func(...);
    enum {value = CHECKER(func,TypeHasMemberFunc)};
};

//APPROACH 2
template <typename T>struct has_member_func
{
    template<class C> static char func(char (*)[sizeof(&C::member_func)]);
    template<class C> static long func(...);
    enum{value = CHECKER(func,has_member_func)};
};
int main(){
 if(Check_If_T_Is_Class_Type<A>::val)
   std::cout<<TypeHasMemberFunc<A>::value; //using APPROACH 1

 if(Check_If_T_Is_Class_Type<B>::val)
   std::cout<<has_member_func<B>::value; //using APPROACH 2
}

但是我的问题是您更喜欢哪种方法(方法 1 或方法 2)以及为什么?
您在给定的方法中是否发现任何不一致之处?如果是,请告诉我。

P.S : 假设sizeof(char)!= sizeof(long)

【问题讨论】:

标签: c++ templates sfinae


【解决方案1】:

第二种方法不检查函数类型(返回类型或参数类型)并且适用于所有类型,而不仅仅是类类型。

【讨论】:

  • 另一个:第二个因函数重载而失败。
【解决方案2】:

我个人更喜欢第二种方法,因为它更短且更容易理解。但是 GCC 不会编译它,所以你必须为 GCC 使用类似的东西:

namespace detail
{
    template<class C> char func(char (*)[sizeof(&C::member_func)]);
    template<class C> long func(...);   
}

template <typename T>struct has_member_func
{
    enum{value = (sizeof(detail::func<T>(0)) == 1)};
};

另外,摆脱 CHECKER 宏会很好。它使您的代码可读性极差。

无论如何,我会避免在生产代码中使用此类 C++ hack(除非您是 Boost 团队成员 :-)

这样的东西容易出错,难以支持,难以在编译器之间移植,但主要的一点是,我不记得任何现实生活中的任务需要这样的硬编码 C++。

【讨论】:

  • 我检查了,没有,第二种方法可以在 GCC 中编译:-|
【解决方案3】:

编辑:完成并更正。

另一种方法,使用继承中的歧义,可能在功能上等同于您的方法 2。我记得方法 1 有问题(尽管它使用 G++ 4.4.5 编译),因为名称解析触发了错误而不是替换失败。我不得不求助于:

template <class T>
struct has_foo
{
  struct fallback { void foo(...); };
  struct D : T, fallback { };

  template <typename U, U> struct K;

  // Will be ambiguous for U = D iff T has a foo member function.                                                                                                         
  // It doesn't trigger an error, since D will always have at least one                                                                                                   
  // foo member function.                                                                                                                                                 
  template <class U> static char (&test(K<void (fallback::*)(...), &U::foo>*))[1];
  template <class U> static char (&test(...))[2];

  static const bool value = sizeof(test<D>(0)) == 2;
};

这在 T 是类时有效,因此您可能需要添加层来检查 T 是否是类类型。

请注意,任何foo 成员函数都会被检测到。如果你想检查检测到的foo函数是否可以用给定的参数调用,你必须再做一层SFINAE:

// Check whether foo can be called with an argument of type Arg
// and yields an element of type Res.
// If you need Res = void, this code does not work.
template <class T, typename Arg, typename Res>
struct check_foo
{
    struct flag {};
    struct D : T { using T::foo; flag foo(...); };

    template <typename U>
    static char (&test(U))[1];

    template <typename> static char (&test(...))[2];

    static Arg f();

    static const bool value = sizeof(test<Arg>( ((D*)0)->foo(f()) )) == 1;
};

【讨论】:

  • so you may want to add another layer for checking whether T is a class type 已经处理好了。查看我的代码示例。 Check_If_T_Is_Class_Type 检查类型 T 是否是类类型。
  • @Alexandre C...你说触发一个简单的错误而不是替换失败,因为 member_func 通常不会是 C 的成员...所以你的意思是, member_func 必须是 C 的成员才会导致“替换失败”?这有什么意义?
  • 我相信你需要K&lt;fallback, &amp;U::foo&gt;?如果传递U,则不能传递&amp;U::foo,因为它需要从void(fallback::*)(...)void(D::*)(...) 的隐式转换。
  • 另外,您的代码将无法检测到特定的函数类型。该代码更像是“检查该类中是否存在具有给定名称的成员”。它不会只检查特定的成员函数。
  • @Nawaz,@litb,已编辑。匆忙编写的代码现在可以工作了。 Johannes 你说得对,这个方法只测试具有特定名称的成员函数的存在。我需要一次检查一个类是否是一个可以使用给定签名(可能带有转换)调用的仿函数,并且我使用了这种精确的方法,并使用另一种 sfinae 机制来检查签名。至于简单的错误,我记得像 OP 那样拼写 foo 有问题,它触发了错误而不是替换失败,我不得不使用继承方法。
猜你喜欢
  • 1970-01-01
  • 2018-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-23
  • 2015-07-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多