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

14

Решение

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

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

Другие решения

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

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.
1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector