Проверьте во время компиляции, наследуется ли необязательный шаблон класса от его первого параметра шаблона - PullRequest
0 голосов
/ 19 октября 2019

Короткий вопрос

Как я могу проверить во время компиляции, если шаблон класса ( edit: не создан) наследуется от своего первого параметра шаблона начиная с C ++ 17?

Длинный вопрос

Я хочу выяснить, наследует ли шаблон класса от своего (первого) параметра шаблона, построив что-то вроде:

template<template<typename> class TemplateClass>
struct template_parameter_is_base_of
{
    /* Implementation */
};

В идеале, template_parameter_is_base_of можно использовать так:

template<typename T> struct WithParent : T { /* Implementation */ };
template<typename T> struct WoutParent :   { /* Implementation */ };

static_assert(template_parameter_is_base_of<WithParent>::value); // Ok as expected since WithParent inherits from T
static_assert(template_parameter_is_base_of<WoutParent>::value); // Error as expected since WithParent doesn't inherit from T

Моя неудачная попытка

Моя попытка реализовать template_parameter_is_base_of:

struct Dummy {};

template<template<typename> class TemplateClass>
struct template_parameter_is_base_of
{
    static constexpr bool value = std::is_base_of_v<Dummy, TemplateClass<Dummy>>;
};

..., которая работает в этом случае:

template<typename T>
struct A : T {
    void work() { /* Irrelevant implementation */ }
};

static_assert(template_parameter_is_base_of<A>::value); // Passes because A inherits from its template parameter. Nice!

... но происходит сбой, если шаблон класса имеет метод со спецификатором override:

template<typename T>
struct B : T {
    void work() override { /* Irrelevant implementation */ }
};

static_assert(template_parameter_is_base_of<B>::value); // Fails, but I want it to pass because B inherits from its template parameter.

Это то, что я думаю прямо сейчас

Я думаю, что загнал себя в угол с использованием подхода Dummy, использованного выше, поскольку создание экземпляра шаблона класса TemplateClass<Dummy> в std::is_base_of_v всегда будет неудачным, если TemplateClass содержит какие-либо методы со спецификатором override.

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

Наконец, вопрос

Возможно ли реализовать template_parameter_is_base_of на C ++ 17? Если да, как это можно сделать?

Ответы [ 3 ]

2 голосов
/ 20 октября 2019

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

Шаблон - это по сути параметризованный инструмент для производства определенных конструкций C ++: класса, функции или переменной. Шаблон сам по себе еще не является тем, что он будет создавать. Шаблон класса не наследуется ни от чего, потому что это еще не класс . Таким образом, сам вопрос не является функциональным.

В сочетании с этим существует тот факт, что существует явная / частичная специализация шаблонов. Даже если шаблон базового класса действительно наследовал от своего первого параметра шаблона, это ничего не гарантирует. Вы до сих пор не знаете, будет ли какой-либо конкретный экземпляр шаблона WithParent<T> использовать базовый шаблон. Пользователь может легко специализировать WithParent для определенного типа или даже использовать частичную специализацию для целого семейства типов.

То, что вы хотите, не может поддерживать C ++. Если вы пытаетесь что-то проверить или предотвратить определенное злоупотребление или что-то еще, вам придется сделать это по-другому.

1 голос
/ 20 октября 2019

Как я могу проверить во время компиляции, наследует ли шаблон класса его первый параметр шаблона с C ++ 17?

Вы имеете в виду, если экземпляр создан шаблон класса наследует от своего первого параметра шаблона?

Простой, используйте std::is_base_of_v:

template <typename T, /*other stuff goes here*/>
class A : /* classes which A inherits from */ { /* ... */}

template <typename T, /*other stuff goes here*/>
constexpr bool A_inherits_its_first_tempalte_param() {
    return std::is_base_of_v<T, A<T, /* ... */ >;
}

и все.

0 голосов
/ 20 октября 2019

Не уверен, что это хороший вариант, но я предлагаю вам следующий способ исследования.

Вы можете попробовать написать черты типа следующим образом

template <typename T>
void foo (T *);

template <template <typename> class, typename = void>
struct tp_is_base_of : public std::false_type
 { };

template <template <typename> class C>
struct tp_is_base_of<
   C, std::void_t<decltype(foo<Dummy>(std::declval<C<Dummy>*>()))>>
     : public std::true_type
 { };

Таким образом, вы можете проверить, еслиУказатель C<Dummy> принимается функцией, которая ожидает указатель Dummy: если C<Dummy> наследуется от Dummy, ответ должен быть да.

Ниже приведен полный пример компиляции

#include <type_traits>

struct Dummy { virtual void work() {} };

template <typename T>
struct A : T
 { void work() { /* Irrelevant implementation */ } };

template <typename T>
struct B : T
 { void work() override { /* Irrelevant implementation */ } };

template <typename T>
struct C
 { };

template <typename T>
void foo (T *);

template <template <typename> class, typename = void>
struct tp_is_base_of : public std::false_type
 { };

template <template <typename> class C>
struct tp_is_base_of<
   C, std::void_t<decltype(foo<Dummy>(std::declval<C<Dummy>*>()))>>
     : public std::true_type
 { };

int main()
 {
   static_assert( true  == tp_is_base_of<A>::value );
   static_assert( true  == tp_is_base_of<B>::value );
   static_assert( false == tp_is_base_of<C>::value );
 }
...