C++ рекурсивная специализация шаблонов
Я написал абстрактный шаблонный класс-контейнер, который должен определять числовые операторы (унарные + и -, двоичные +, - и *), если это имеет смысл для параметра шаблона (то есть, если это числовой тип).
Затем я хотел бы применить эти числовые операции к контейнерам контейнеров с числовыми значениями (и к контейнерам контейнеров с контейнерами с числовыми значениями и т. Д.).
Я написал следующий код. (A)
Маркер показывает, как я пытался решить проблему рекурсивной специализации.
template <typename T>
struct is_numeric : public std::is_arithmetic<T>{};
template <typename T> /* (A) */
struct is_numeric<GenericContainer<T>> : public std::is_arithmetic<T>{};
/* Classic generic container for non-numeric base types */
template <typename T, bool isNumeric=false>
class BaseContainer : public GenericContainer<T> {};
/* Numeric container: +,-,* operations for numeric base types */
template <typename T>
class BaseContainer<T, true> : public NumericContainer<T> {};
/* Arithmetic base types should map on numeric containers */
template <typename T>
class Container : public BaseContainer<T, is_numeric<T>::value> {};
Затем в тестовой программе у меня есть следующие утверждения:
/* Vector inherits from Container */
typedef Vector<int, 3> V3D;
ASSERT(is_numeric<int>::value); /* # => OK */
ASSERT(is_numeric<double>::value); /* # => OK */
ASSERT(is_numeric<V3D>::value); /* # => FAIL */
Два первых утверждения работают как положено
4 ответа
Boost's enable_if и черты типа позволяют делать трюки так, как вам нужно:
template <class T, class Enable = void>
struct is_numeric : public std::is_arithmetic<T> {};
template <class T>
struct is_numeric<T, typename enable_if<is_base_of<GenericContainer<T>, T> >::type>
: public std::is_arithmetic<T> {};
В решении используется принцип SFINAE для компиляции второй версии is_numeric
когда параметр шаблона соответствует критериям внутри enable_if
, Обратите внимание, что синтаксис is_base_of
является is_base_of<Base, Derived>
, Более подробное объяснение содержится в документации Boost's enable_if.
Поскольку отношения в вашем случае еще более сложны, как любезно отметил Дэвид Родригес, вам, вероятно, следует сделать это немного по-другому:
template <template <class> class U, class T>
struct is_numeric<U<T>, typename enable_if<is_base_of<GenericContainer<T>, U<T> > >::type>
: public std::is_arithmetic<T> {};
И если вы не можете использовать сами библиотеки, вы всегда можете использовать их в качестве вдохновения:)
Ты пробовал:
template <typename T>
struct is_numeric : public std::is_arithmetic<T>{};
template <template<class...> class Container, typename T, typename... Rest>
struct is_numeric<Container<T, Rest...>> : public is_numeric<T>{};
Кажется, работает на меня.
Ваше решение дает сбой по очень конкретной причине: специализация параметра типа шаблона будет соответствовать только точному типу, а не любому производному типу.
Если вы хотите, чтобы производные типы также совпадали, вам нужно переключиться и использовать другую стратегию. В возрасте constexpr
переключение на функции позволит вам использовать разрешение перегрузки в своих интересах (в качестве одной из стратегий):
// Basis
constexpr bool is_numeric_impl(...) { return false; }
template <typename T>
constexpr bool is_numeric(T const& t) { return is_numeric_impl(&t); }
// Specializations
template <typename T,
typename = std::enable_if<std::is_arithmetic<T>::value>::type>
constexpr bool is_numeric_impl(T const*) { return true; }
template <typename T>
constexpr bool is_numeric_impl(GenericContainer<T> const*) {
return is_numeric((T const*)nullptr);
}
Основным преимуществом является то, что это решение является открытым, так что другие люди могут использовать те же черты и добавить специализации; потому что он использует белый список.
Вы должны определить is_numeric
черта для каждого контейнера, вы не можете просто использовать base
определение.
template <typename T, size_t N>
struct is_numeric< Vector<T,N> > : is_numeric< GenericContainer<T> > // *
{};
Также обратите внимание, что определение is_numeric
должно быть похоже на то, что в комментарии, а не на вопрос. То есть вы хотите определить is_numeric
для контейнера с точки зрения того, является ли вложенный тип числовым или нет (так что вы можете отделить различные слои).