Вопрос

Прочитав ответ на это вопрос, Я узнал, что 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
}

Что-то так это возможно?

Спасибо.

Это было полезно?

Решение

Это, безусловно, возможно; Вы просто должны быть осторожны, чтобы все ветви были взаимоисключающими, в противном случае вы получите двусмысленность.

Взгляни на Тип повышения типов а также Усилить включить, если, которые являются двумя лучшими инструментами для поддержки этого. Повысить лед (который означает интегральное постоянное выражение) можно использовать для объединения множественных типов, чтобы помочь вам сделать более сложный тип соответствия (и обеспечить, чтобы ваши перегрузки были взаимоисключающимися.

Это может быть несколько сложным и запутанным, поэтому вот относительно простой пример. Скажем, у вас есть классовая иерархия:

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 Возвращает true, если T это базовый класс, поэтому, если вы пытаетесь позвонить foo(Base()), существует двусмысленность, потому что обе шаблоны функций совпадают. Мы можем решить это, используя комбинацию типовых черт и использование улучшения льда:

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- Одеждается на то, что вы хотите сделать с этим, вы можете сделать что-то работу), но в целом это возможно.

Другие советы

Вопрос легко, когда вы понимаете, что

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

(Очевидно, при звонке вам нужно позаботиться, так как варианты не являются взаимоисключающими)

Как вы его реализовали, нет. Компиляция потерпит неудачу, если 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

В зависимости от именно требований вы можете попробовать другой подход. Выше это что-то сделало несколько часов назад

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top