2013-12-21 4 views
14

Сценарий:
У меня есть множество типов, которые можно классифицировать как контейнеры последовательностей.
Все контейнеры последовательностей представляют собой структуры данных, но не каждая структура данных будет контейнером последовательности.Как использовать SFINAE для выбора ближайшего типа соответствия типа?

Вот пример иллюстрированного в коде. В этом примере единственным «важным типом» является Array_T. Он подразделяется на две категории: это контейнер последовательности, и поскольку все контейнеры последовательностей представляют собой структуры данных, это, в свою очередь, структура данных.

//A sequence container type 
class Array_T{}; 

//A type trait for that particular sequence container 
template <typename T> struct Is_Array   { static const bool value = false; }; 
template <>   struct Is_Array<Array_T> { static const bool value = true; }; 

//A type trait to identify all of the sequence containers 
template <typename T> struct Is_A_Sequence_Container { static const bool value = Is_Array<T>::value 
/* would probably "or" together more sequence types, but we only have Array_T in this example */;}; 

//A type trait to identify all of the data structures 
template <typename T> struct Is_A_Data_Structure { static const bool value = Is_A_Sequence_Container<T>::value 
/* would probably "or" together more data structure types, but we only have sequence containers in this example */;}; 

Обратите внимание, что на Array_T не может быть выполнено наследование; он должен оставаться таким, каким он был объявлен.


Проблема:
Я хочу написать две функции. Одна функция будет обрабатывать все контейнеры последовательностей, а другая функция будет обрабатывать все структуры данных. Я не знаю, будет ли фактически существовать функция контейнера последовательности или нет, поскольку эта часть кода может или не может быть сгенерирована.

Итак, как я могу использовать мета-шаблонное программирование, чтобы выбрать наиболее подходящую идентификацию для типа? Вот два примера ожидаемого поведения:

Случай 1:

// ... 
//Both functions exist! Call the more specific one. 
// ... 

function(Array_T{}); // prints "sequence container" 

Случай 2:

// ... 
//Only the data structure one exists(not the sequence container one) 
// ... 

function(Array_T{}); // prints "data structure" 

Моя попытка до сих пор:

#include <iostream> 
#include <type_traits> 

//A sequence container type 
class Array_T{}; 

//A type trait for that particular sequence container 
template <typename T> struct Is_Array   { static const bool value = false; }; 
template <>   struct Is_Array<Array_T> { static const bool value = true; }; 

//A type trait to identify all of the sequence containers 
template <typename T> struct Is_A_Sequence_Container { static const bool value = Is_Array<T>::value 
/* would probably "or" together more sequence types, but we only have Array_T in this example */;}; 

//A type trait to identify all of the data structures 
template <typename T> struct Is_A_Data_Structure { static const bool value = Is_A_Sequence_Container<T>::value 
/* would probably "or" together more data structure types, but we only have sequence containers in this example */;}; 

// ↑ all of this code was already shown to you 


//NOTE: This function MAY OR MAY NOT actually appear in the source code 
//This function handles all sequence types 
template<class T, typename std::enable_if<Is_A_Sequence_Container<T>::value,int>::type=0> 
void function(T t) { 
    std::cout << "sequence container" << std::endl; 
    return; 
} 

//This function handles all data structures; assuming a more specific function does not exist(*cough* the one above it) 
template<class T, typename std::enable_if<Is_A_Data_Structure<T>::value,int>::type=0> 
void function(T t) { 
    std::cout << "data structure" << std::endl; 
    return; 
} 

int main(){ 

    function(Array_T{}); 
} 

Теперь Я понимаю, что это не работает, потому что значение будет истинно для ОБОИХ из enable_ifs.
Поэтому я хочу добавить вторую функцию enable_if в функцию структуры данных, чтобы проверить, существует ли функция контейнера последовательности. Что-то вроде этого:

//... 
//This function handles all data structures; assuming a more specific function does not exist(*cough* the one above it) 
template<class T, typename std::enable_if<Is_A_Data_Structure<T>::value,int>::type=0, 
        typename std::enable_if</*if the more specific function does not exist*/,int>::type=0>> 
void function(T t) { 
    std::cout << "data structure" << std::endl; 
    return; 
} 

int main(){ 

    function(Array_T{}); 
} 

И вот где я застрял. Есть ли способ сделать это, не касаясь замедления Array_T, и без привлечения третьей функции для диспетчеризации?

+0

Я думаю, что C++ 14 понятий lite решит вам проблему – Manu343726

ответ

8

Я хотел бы использовать тег диспетчеризацию:

struct DataStructureTag {}; 
struct SequenceContainerTag : public DataStructureTag {}; 

template <typename T> struct DataStructureTagDispatcher 
{ 
    typedef typename std::conditional<Is_A_Sequence_Container<T>::value, 
             SequenceContainerTag, 
             DataStructureTag>::type type; 
}; 


// NOTE: This function MAY OR MAY NOT actually appear in the source code 
// This function handles all sequence types 
template<class T> 
void function(T&& t, const SequenceContainerTag&) { 
    std::cout << "sequence container" << std::endl; 
    return; 
} 

// This function handles all data structures (not handled my a more specific function) 
template<class T> 
void function(T&& t, const DataStructureTag&) { 
    std::cout << "data structure" << std::endl; 
    return; 
} 

template <class T> 
typename std::enable_if<Is_A_Data_Structure<T>::value, void>::type 
function(T&& t) 
{ 
    typedef typename DataStructureTagDispatcher<T>::type tag; 
    function(t, tag()); 
} 
+1

Вам, возможно, понадобится несколько 'decay' или 'remove_reference', прежде чем вы будете использовать некоторые из этих классов признаков. – Yakk

+0

Почему наследование тегов? Разве это не так? –

+0

@TrevorHickey: наследование тега позволяет поддерживать случаи, когда специализация не существует. ('void function (T && t, const SequenceContainerTag &)' может не существовать). без наследования каждая функция «отправлена» должна существовать. – Jarod42

1

Вы также можете использовать иерархию классов для неоднозначности перегруженных

struct R2 {}; 
struct R1 : R2 {}; 

//NOTE: This function MAY OR MAY NOT actually appear in the source code 
//This function handles all sequence types 
template<class T, typename std::enable_if<Is_A_Sequence_Container<T>::value,int>::type=0> 
void function(R1, T t) { 
    std::cout << "sequence container" << std::endl; 
    return; 
} 

//This function handles all data structures; assuming a more specific function does not exist(*cough* the one above it) 
template<class T, typename std::enable_if<Is_A_Data_Structure<T>::value,int>::type=0> 
void function(R2, T t) { 
    std::cout << "data structure" << std::endl; 
    return; 
} 

int main(){ 
    function(R1{}, Array_T{}); 
} 
1

Я приспособил https://stackoverflow.com/a/264088/2684539:

// template funcName should exist 
#define HAS_TEMPLATED_FUNC(traitsName, funcName, Prototype)       \ 
    template<typename U>                \ 
    class traitsName                 \ 
    {                    \ 
     typedef std::uint8_t yes;             \ 
     typedef std::uint16_t no;             \ 
     template <typename T, T> struct type_check;         \ 
     template <typename T = U> static yes &chk(type_check<Prototype, &funcName>*); \ 
     template <typename > static no &chk(...);         \ 
    public:                   \ 
     static bool const value = sizeof(chk<U>(0)) == sizeof(yes);     \ 
    } 

А потом

//NOTE: This function MAY OR MAY NOT actually appear in the source code 
//This function handles all sequence types 
template<class T, typename std::enable_if<Is_A_Sequence_Container<T>::value,int>::type = 0> 
void function(T t) { 
    std::cout << "sequence container" << std::endl; 
    return; 
} 

// this assumes that any template 'function' exists 
// (Do you have version for `data structure` ?) 
// or else create a dummy struct and then 
// template <T> 
// typename std::enable_if<std::is_same<T, dummy>::value>::type function(dummy) {} 
HAS_TEMPLATED_FUNC(isFunctionExist_Specialized, function<T>, void (*)(T)); 

// This function handles all data structures not already handled 
template<class T, typename std::enable_if<Is_A_Data_Structure<T>::value, int>::type = 0, 
        typename std::enable_if<!isFunctionExist_Specialized<T>::value, int>::type = 0> 
void function(T t) { 
    std::cout << "data structure" << std::endl; 
    return; 
} 
// Care, isFunctionExist_Specialized<T>:: value is computed only once, 
// so you have to use another 
// `HAS_TEMPLATED_FUNC(isFunctionExist, function<T>, void (*)(T));` 
// to take into account these new functions. 
+0

Это выглядит многообещающим, но, похоже, он не компилируется для меня. http://ideone.com/72ljig –

+0

некоторые опечатки во время копирования/вставки/переформатирования в макросе. Прости. должны быть исправлены. см. http://ideone.com/UmvrGC – Jarod42

Смежные вопросы