Проверка наличия вложенного шаблонного класса

Я пытаюсь создать черту, которая проверяет наличие вложенного шаблонного класса. Это моя попытка проверить, есть ли у класса O вложенный класс inner с параметром шаблона T:

template <typename O, typename T> struct has_inner {
    static const bool value = std::is_class<typename O::template inner<T> >::value;
};

Однако это не работает должным образом. Учитывая два примера классов dummy и ok

struct dummy {};
struct ok {
    template <typename T>
    struct inner {
    };
};

Чек на ok

std::cout << std::boolalpha << has_inner<ok, float>::value << std::endl;

будет работать, а проверка на dummy

std::cout << std::boolalpha << has_inner<dummy, int>::value << std::endl;

не скомпилируется на clang 3.2 с ошибкой

error: 'inner' following the 'template' keyword does not refer to a template
    static const bool value = std::is_class<typename O::template inner<T> >::value;
                                                                 ^~~~~
note: in instantiation of template class 'has_inner<dummy, int>' requested here
    std::cout << std::boolalpha << has_inner<dummy, int>::value << std::endl;

Похоже, что компилятор пытается фактически сформировать это шаблонное выражение, прежде чем передать его std::is_class. Следовательно, я вижу два решения:

  1. Скажите компилятору отложить раскрытие шаблона, или
  2. Совершенно используйте другой подход.

Однако я тоже не знаю, как выполнять, может ли кто-нибудь помочь?


person elemakil    schedule 09.06.2014    source источник


Ответы (2)


ЭТА ПРОБЛЕМА

Обычно вы реализуете подобные черты, используя и полагаясь на SFINAE , то, чем ваша реализация не пользуется.

Как указано, компилятор попытается создать экземпляр typename O::template inner<T>, независимо от того, возможно это или нет; и если это невозможно, компилятор выдаст вам диагностику ошибки прямо в лицо.

Что вам нужно сделать, так это условную проверку, чтобы увидеть, действительно ли T имеет внутри шаблон-класс, без его создания, если его нет.


РЕШЕНИЕ - СФИНАЕ НА СПАСЕНИЕ!

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

namespace impl {
  template<class T, class... Args>
  struct has_inner {
    template<class U, typename = typename U::template inner<Args...>> // (A)
    static std::true_type  test (int);

    template<class U>
    static std::false_type test (...);                                // (B)

    using result_type = decltype (test<T> (0));                       // (C)
  };
}

template<class... Ts>
using has_inner = typename impl::has_inner<Ts...>::result_type;

Примечание: при использовании decltype(test<T>(0)) мы получим либо std :: true_type, либо std :: false_type, которые являются стандартным поведением, когда работа с результатами признаков типа.


Правила SFINAE гласят, что если шаблон функции приведет к недопустимому объявлению функции при создании экземпляра, то это как если бы этой функции не существовало, компилятор попытается найти другое совпадение, вместо того, чтобы отказываться от .

Это то, что происходит в (C), мы пытаемся вызвать (A), но если это не удается (т. Е. Недопустимое выражение yield внутри template<class U, typename ...>, мы закончим вызовом (B).

(B) не так хорошо подходит, как успешное создание экземпляра (A), но если (A) не может быть создан ... (B) подойдет.

person Filip Roséen - refp    schedule 09.06.2014

Вам нужно использовать класс признаков и SFINAE, примерно так:

template<class A, typename B>
struct has_inner
{
private:
    template<class T, typename U>
    static std::true_type has(typename T::template inner<U>*);

    template<class, typename>
    static std::false_type has(...);
public:
    static constexpr auto value = decltype(has<A, B>(nullptr))::value;
};

Теперь вы можете использовать его с правильными результатами:

static_assert(has_inner<ok, float>::value, "ok does not have inner");    // OK
static_assert(has_inner<dummy, float>::value, "dummy does not have inner"); // ERROR 
person 0x499602D2    schedule 09.06.2014