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 для контейнера с точки зрения того, является ли вложенный тип числовым или нет (так что вы можете отделить различные слои).

Другие вопросы по тегам