Как специализировать класс для внутреннего типа шаблона?

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

template <typename T> struct Check : std::false_type { };

У меня есть класс шаблона, который содержит внутренний класс:

template <unsigned N>
struct Kitty
{
  struct Purr;
};

Я хочу пометить внутренний класс Purr как поддерживающий функцию, обозначенную как Check. Другими словами, я хочу сделать так, чтобы Check<Kitty<123>::Purr>::value было true. Я попытался сделать следующее, но я получаю сообщение об ошибке:

template <unsigned X>
struct Check<typename Kitty<X>::Purr> : std::true_type { };

ошибка: параметры шаблона не выводятся в частичной специализации:

Возможно ли это сделать, или это ограничение С++, что вы не можете специализироваться на членах класса внутреннего шаблона?


person Myria    schedule 19.01.2017    source источник
comment
Нет, ошибка явно указывает на проблему. Это ограничение, а не то, что вы предлагаете. Что-то вроде struct Check<typename Kitty<0>::Purr> : true_type { }; в порядке.   -  person DeiDei    schedule 20.01.2017
comment
Очевидно, что вы имеете в виду, что каждая специализация Kitty::Purr должна быть охвачена. Но подумайте, что происходит, если специализация Kitty не имеет Purr (template<> struct Kitty<0> {};). Язык решает эту проблему, говоря, что вы просто не можете этого сделать. В таком случае было бы неплохо просто проигнорировать эту специализацию или как-то сказать, что каждая специализация должна ей соответствовать, но увы. Потенциально вы могли бы создать базовый класс struct KittyBase { struct Purr; }; и наследовать от него Kitty. Нужен Purr для доступа к N? Сделать Purr шаблон в базе.   -  person chris    schedule 20.01.2017


Ответы (2)


Как указано в моем комментарии, это можно сделать выведенным контекстом, используя базовый класс, который я назову KittyBase. Использование базового класса на самом деле является обычным явлением для шаблонов, чтобы избежать дублирования ненужного кода для каждого нового экземпляра. Мы можем использовать ту же технику, чтобы получить Purr без необходимости дедукции N.

Однако простое помещение Purr в базовый класс лишает его доступа к N. К счастью, даже при создании шаблона Purr это все еще может быть невыведенным контекстом: >Живой пример

#include <type_traits>

template <typename T> struct Check : std::false_type { };

struct KittyBase 
{    
    template<unsigned N> // Template if Purr needs N.
    struct Purr;

protected:
    ~KittyBase() = default; // Protects against invalid polymorphism.
};

template <unsigned N>
struct Kitty : private KittyBase
{
    using Purr = KittyBase::Purr<N>; // Convenience if Purr needs N.
    Purr* meow;
};

template <unsigned X>
struct Check<typename KittyBase::Purr<X>> : std::true_type { };

static_assert(not Check<int>{});
static_assert(Check<Kitty<123>::Purr>{});
static_assert(Check<Kitty<0>::Purr>{});

int main() {}

При желании вы даже можете сделать KittyBase::Purr приватным и использовать template<typename T> friend struct Check; для предоставления доступа к черте. К сожалению, я не знаю, можно ли ограничить это только определенными специализациями черты.

person chris    schedule 19.01.2017

В этом ответе есть интересный подход к определению того, существует ли тип с использованием SFINAE.

Адаптированный для проверки существования типа T::Purr, он позволяет вам писать черту типа без проблемной специализации.

#include <type_traits>

template <unsigned T>
struct Kitty
{
    struct Purr{};
};

// A specialization without Purr, to test
template <>
struct Kitty<5>{ };

// has_purr is taken and adapted from https://stackoverflow.com/a/10722840/7359094
template<typename T>
struct has_purr
{   
    template <typename A> 
    static std::true_type has_dtor(decltype(std::declval<typename A::Purr>().~Purr())*);

    template<typename A>
    static std::false_type has_dtor(...);

    typedef decltype(has_dtor<T>(0)) type;

    static constexpr bool value = type::value;
};

// Check if a type is an instance of Kitty<T>
template<typename T>
struct is_kitty : std::false_type {};
template<unsigned T>
struct is_kitty<Kitty<T>> : std::true_type {};

template <typename T> 
struct Check : std::bool_constant< is_kitty<T>::value && has_purr<T>::value> {};

static_assert( Check<int>::value == false, "int doesn't have purr" );
static_assert( Check<Kitty<0>>::value == true, "Kitty<0> has purr" );
static_assert( Check<Kitty<5>>::value == false, "Kitty<5> doesn't has purr" );
person François Andrieux    schedule 19.01.2017