多个 SFINAE 规则

发布于 2024-08-30 22:03:36 字数 1471 浏览 6 评论 0原文

阅读此答案后 问题,我了解到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
}

这样的事情可能吗?

谢谢。

After reading the answer to this question, I learned that SFINAE can be used to choose between two functions based on whether the class has a certain member function. It's the equivalent of the following, just that each branch in the if statement is split into an overloaded function:

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()
}

becomes

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()

I was wondering if it was possible to extend SFINAE to do multiple rules. Something that would be the equivalent of this:

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
}

Is something like this possible?

Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

半仙 2024-09-06 22:03:36

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

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

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

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

并且您想要为 Base 调用函数 foo 的一个重载,并为从 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&) { } 

但是,如果 T 是基类,则 is_base_of 返回 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 - 取决于您想用它做什么,您也许能够使某些东西发挥作用),但总的来说这是可能的。

This is certainly possible; you just have to be careful to ensure that all of the branches are mutually exclusive, otherwise you'll end up with an ambiguity.

Take a look at Boost Type Traits and Boost Enable If, which are the two best tools for supporting this. Boost ICE (which stands for Integral Constant Expression) can be used to combine multiple type traits to help you to do more complex type matching (and to ensure that your overloads are mutually exclusive.

This can be somewhat complicated and convoluted, so here's a relatively straightforward example. Say you have a class hierarchy:

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

and you want to call one overload of a function foo for Base, and another overload for any class derived from Base. A first attempt might look like:

#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&) { } 

However, is_base_of returns true if T is the base class, so if you attempt to call foo(Base()), there is an ambiguity because both function templates match. We can resolve this by using a combination of the type traits and using the Boost ICE helpers:

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&) { }

These overloads are mutually exclusive, and they ensure there is no ambiguity.

Some of your examples are not supported (namely, HAS_MEMBER_FUNCTION_X; I'm not sure about IS_TEMPLATE_CLASS_C--depending on what you want to do with it you might be able to make something work), but in general this is possible.

失而复得 2024-09-06 22:03:36

时,问题就很容易了

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>);

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

The question is easy when you realize that

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

means exactly the same as

if (a) { X(); }
if (!a && b) { Y(); }

However, you could also extend your true/false dichotomy.

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>);

(Obviously, when calling you have to take care as the options are not mutually exclusive)

杀お生予夺 2024-09-06 22:03:36

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

然而,可以使用模板专门化(隐藏在 boost mpl 的魔法中)来做到这一点。

你有时可以使用带有元功能的 boost mpl 向量来做到这一点:查看
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

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

the way you have it implemented, no.
Compilation will fail if arg does not have one of the functions. (I think you know this, just making sure).

However, it is possible to do so using template specialization (hidden in magic of boost mpl).

you could do sometime this using boost mpl vector with meta-functions: check out
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

depending on exactly requirements, you can try different approach.
Above is something have done few hours ago

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文