Вложенные шаблоны C ++ - PullRequest
       25

Вложенные шаблоны C ++

2 голосов
/ 01 декабря 2019

Итак, у меня есть проблема с кодом, подобным этому:

У меня есть структура, подобная этой

template <int N>
struct Inner
{
    enum
    {
        val = 2*N
    };
};

И я хочу добиться вот что:

int v = Outer<Inner<4>>::val;
int b = Outer<false>::val;
cout<< v <<endl;
cout<< b <<endl;

Моя цель состоит в том, чтобы создать структуру "Outer", которая принимает bool или Inner<int N> и установить Outer::val в Inner::val или bool Так что я создал что-то вроде этого (не работает):

template <bool B>
struct Outer
{
    enum
    {
        val = B
    };
};

template <Inner<int> I>
struct Outer
{
    enum
    {
        val = I::val
    };
};

Что не так с этим и как это исправить? (Я видел несколько похожих вопросов, но все еще не могу применить это к моей проблеме)

Ответы [ 2 ]

2 голосов
/ 01 декабря 2019

Параметр шаблона может быть типом, значением (не типом) или шаблоном [temp.param] . То, что вы пытаетесь достичь, потребует, чтобы ваш шаблон Outer имел параметр, который может быть типом или значением. К сожалению, это невозможно.

Что вы можете сделать, это обернуть значение bool в тип:

template <bool b>
struct InnerBoolean
{
    static constexpr bool val = b;
};

, а затем получить одно общее определение для Outer

template <typename T>
struct Outer
{
    enum
    {
        value = T::val
    };
};

и затем используйте Outer<Inner<4>> и Outer<InnerBoolean<False>>.

Вместо того, чтобы писать свою собственную оболочку, если вы переименуете val в value, вы можете использовать оболочки, которые стандартная библиотека предоставляет вstd::bool_constant и std::true_type и std::false_type.

В то время как до C ++ 17 параметр шаблона нетипичного типа не может иметь тип класса [temp.param] / 4 , C ++ 20 снимет это ограничение и разрешит параметры шаблона любого литерального типа . Таким образом, пока Inner может быть литеральным типом, вы сможете просто передать значение типа Inner напрямую и использовать параметр автоматического шаблона:

struct Inner
{
    int N;

    constexpr Inner(int N) : N(N) {}

    constexpr operator int() const { return 2*N; }
};

template <auto val>
struct Outer
{
    enum
    {
        value = val
    };
};

auto a = Outer<Inner(4)>::value;
auto c = Outer<false>::value;
2 голосов
/ 01 декабря 2019

В вашем коде есть некоторые проблемы.

Прежде всего: вы определяете две разные Outer структуры

template <bool B>
struct Outer
 { /* ... */ };

template <Inner<int> I>
struct Outer
 { /* ... */ };

И вы не можете.

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

Потому что, глядя на ваши desiderata,

int v = Outer<Inner<4>>::val;
int b = Outer<false>::val;

вы хотите передать ему тип в одном случае (Inner<4>) и значение в другом случае. И вы не можете.

Вы должны решить, получит ли Outer тип или значение. До C ++ 17, если получите значение, вы должны решить тип значения;начиная с C ++ 17, вы можете объявить Outer как получающее значение универсального типа (auto как тип значения).

Проблема: значение Inner<int> не может бытьпараметр шаблона (но см. также ответ Майкла Кензеля, который показывает возможное решение C ++ 20, основанное на аргументах значений шаблона).

Таким образом, единственное решение, которое я вижу (до C ++ 20) - это объявление Outer как получающий тип

template <typename>
struct Outer;

Затем вы можете определить Outer специализацию для Inner типов

template <int N>
struct Outer<Inner<N>>
 { enum { val = Inner<N>::val }; }; // or simply enum { val = N };

Для bool значений, вы должны обернуть их вкласс;Я предлагаю (начиная с C ++ 11) использование стандартного класса std::integral_constant и определение следующей Outer специализации

template <bool B>
struct Outer<std::integral_constant<bool, B>>
 { enum { val = B }; };

Использование выглядит следующим образом:

int v = Outer<Inner<4>>::val;
int b = Outer<std::integral_constant<bool, false>>::val;

std::cout << v << std::endl;
std::cout << b << std::endl;

Вы также можете использовать std::false_type, определяя b

int b = Outer<std::false_type>::val;

и, начиная с C ++ 17, также std::bool_constant (сокращение для std::integral_constant для bool значений)

int b = Outer<std::bool_constant<false>>::val;
...