多个SFINAE规则

时间:2010-04-27 02:41:08

标签: c++ templates sfinae

在阅读了这个question的答案之后,我了解到SFINAE可以用来根据类是否具有某个成员函数来选择两个函数。它等同于以下内容,只是将if语句中的每个分支拆分为重载函数:

template<typename T>
void Func(T& arg)
{
    if(HAS_MEMBER_FUNCTION_X(T))
        arg.X();
    else
        //Do something else because T doesn't have X()
}

变为

template<typename T>
void Func(T &arg, int_to_type<true>); //T has X()

template<typename T>
void Func(T &arg, int_to_type<false>); //T does not have X()

我想知道是否有可能扩展SFINAE以执行多项规则。一些与此相当的东西:

template<typename T>
void Func(T& arg)
{
    if(HAS_MEMBER_FUNCTION_X(T))                //See if T has a member function X  
        arg.X();
    else if(POINTER_DERIVED_FROM_CLASS_A(T))    //See if T is a pointer to a class derived from class A
        arg->A_Function();              
    else if(DERIVED_FROM_CLASS_B(T))            //See if T derives from class B
        arg.B_Function();
    else if(IS_TEMPLATE_CLASS_C(T))             //See if T is class C<U> where U could be anything
        arg.C_Function();
    else if(IS_POD(T))                          //See if T is a POD type
        //Do something with a POD type
    else
        //Do something else because none of the above rules apply
}

这样的事情可能吗?

谢谢。

3 个答案:

答案 0 :(得分:6)

这当然是可能的;你只需要小心确保所有分支都是互斥的,否则你最终会产生歧义。

查看Boost Type TraitsBoost Enable If,这是支持此功能的两个最佳工具。 Boost ICE(代表积分常量表达式)可用于组合多个类型特征,以帮助您进行更复杂的类型匹配(并确保您的重载是互斥的。

这可能有点复杂和复杂,所以这是一个相对简单的例子。假设您有一个类层次结构:

struct Base { };
struct Derived : Base { };

并且您希望为foo调用函数Base的一个重载,并为从Base派生的任何类调用另一个重载。第一次尝试可能如下:

#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>

using namespace boost;
using namespace boost::type_traits;

template <typename T>
typename enable_if<is_same<Base, T>, void>::type
foo(const T&) { }

template <typename T>
typename enable_if<is_base_of<Base, T>, void>::type
foo(const T&) { } 

但是,如果is_base_of是基类,则T返回true,因此如果您尝试调用foo(Base()),则存在歧义,因为两个函数模板都匹配。我们可以通过结合使用类型特征和使用Boost ICE帮助程序来解决这个问题:

template <typename T>
typename enable_if<is_same<Base, T>, void>::type
foo(const T&) { }

template <typename T>
typename enable_if<
    ice_and<
        is_base_of<Base, T>::value,
        ice_not<is_same<Base, T>::value>::value 
    >, void>::type
foo(const T&) { }

这些重载是互斥的,它们确保没有歧义。

您的某些示例不受支持(即HAS_MEMBER_FUNCTION_X;我不确定IS_TEMPLATE_CLASS_C - 取决于您要对其执行的操作,您可以使某些内容正常工作) ,但总的来说这是可能的。

答案 1 :(得分:1)

你实施它的方式,没有。 如果arg没有其中一个函数,编译将失败。 (我想你知道这一点,只是确定)。

但是,可以使用模板专业化(隐藏在boost mpl的魔法中)来实现。

你可以使用带有元功能的boost mpl vector来做一些事情:结账 http://www.boost.org/doc/libs/1_40_0/libs/mpl/doc/refmanual.html

typedefs typename mpl::vector<f0,f1,...>::type handlers; // different handlers
// convert logic to int N to map condition to handler
// can use ternary or bit shift trick
// more general approach could be to use vector of mpl::bool_ and mpl::find

typedef typename mpl::vector_c<bool, (first_condition),
                                     (second_condition),...>::type condition;

typedef typename mpl::find<condition, mpl:: bool_<true> >::type iterator;
typedef typename mpl::at<handlers, iterator::pos::value>::type handler;
handler::apply(...); // call handler with some arguments

根据具体要求,您可以尝试不同的方法。 以上是几个小时前完成的事情

答案 2 :(得分:1)

当你意识到这个问题时,问题很简单

if (a) { X(); }
else if (b) { Y(); }

表示与

完全相同
if (a) { X(); }
if (!a && b) { Y(); }

但是,您也可以延长true/false二分法。

enum FuncVariants { HasMember, PointerDerivedFromA, DerivedFromB, InstanceOfC, isPod }
template<typename T>
void Func(T &arg, int_to_type<HasMember>);

template<typename T>
void Func(T &arg, int_to_type<DerivedFromA>);

template<typename T>
void Func(T &arg, int_to_type<DerivedFromB>);

template<typename T>
void Func(T &arg, int_to_type<InstanceOfC>);

(显然,在打电话时你必须小心,因为选项不是互相排斥的)