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

Скажем, у меня есть следующий шаблонный класс:

template<typename T>
class Foo {
    struct store_t {
        uint8_t data[];
    } store;
    /// other stuff using T
}

Есть ли способ создать специализированную версию внутренней структуры, которая будет соответствовать чему-то вроде этого:

class Foo {
    struct store_t {
        uint16_t f1;
        uint16_t f2;
    } store;
    /// other stuff using T
}

Я бы предпочел оставить большинство «других вещей, использующих T» неспециализированными. Однако я бы специализировал некоторые аксессуары. Я чувствую, что хотел бы написать что-то вроде

template<>
struct store_t {
    uint16_t f1;
    uint16_t f2;
} Foo<someT>::store;

но это конечно не работает.


person Choumarin    schedule 26.04.2019    source источник
comment
Используйте другой параметр шаблона для типа внутренней структуры, например template <typename T, typename TInternal> class Foo.   -  person aep    schedule 26.04.2019


Ответы (2)


Ради интереса я покажу еще одно возможное решение, основанное на своего рода самонаследовании.

Предположим, вы хотите специализировать Foo для типа bool.

Вы можете написать основной Foo, добавив нетиповой параметр шаблона со значением по умолчанию (скажем, bool со значением по умолчанию true)

template <typename T, bool = true>
struct Foo
 {
    struct store_t
     { std::uint8_t data[10]; }   store;

    /// other stuff using T
    T value;
 };

Я добавил T value в качестве примера «других вещей, использующих T».

Теперь вы можете специализировать Foo<bool>, наследуя от Foo<bool, false>

template <>
struct Foo<bool> : public Foo<bool, false>
 {
    struct store_t
     { std::uint16_t f1, f2; }   store;
 };

Таким образом, вы можете специализировать store_t/store (и других членов, если хотите), наследуя от Foo<bool, false> "другие вещи, использующие T" (например, bool value).

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

#include <cstdint>
#include <type_traits>

template <typename T, bool = true>
struct Foo
 {
    struct store_t
     { std::uint8_t data[10]; }   store;

    T value;
 };

template <>
struct Foo<bool> : public Foo<bool, false>
 {
    struct store_t
     { std::uint16_t f1, f2; }   store;

    // inherits a bool value from Foo<bool, false>
 };

int main()
 {
   Foo<int>   fi;
   Foo<bool>  fb;

   static_assert( std::is_same<decltype(fi.value),
                               int>::value, "!");
   static_assert( std::is_same<decltype(fi.store.data),
                               std::uint8_t[10]>::value, "!");
   static_assert( std::is_same<decltype(fb.value),
                               bool>::value, "!");
   static_assert( std::is_same<decltype(fb.store.f2),
                               std::uint16_t>::value, "!");
 }
person max66    schedule 26.04.2019

Как и в большинстве случаев в жизни, ответ на вопрос «как решить проблему с шаблонами?» — «использовать больше шаблонов».

Решение 1. Напишите store_t в качестве шаблона

К счастью, нам не нужно делать ничего сумасшедшего. Давайте напишем store_t вне Foo в качестве шаблона:

template<bool use_uint8>
struct Foo_store_t {
    uint8_t data[]; 
};
template<>
struct Foo_store_t<false> {
    uint16_t f1;
    uint16_t f2;
};

Теперь, когда мы пишем Foo, мы можем просто выбрать, какой из них мы хотим использовать, проверив некоторое условие:

template<class T>
class Foo {
    constexpr static bool use_uint8 = /* stuff */; 
    using store_t = Foo_store_t<use_uint8>; 
    store_t store;
};

Решение 2. Напишите две версии store_t, используйте std::conditional

Этот тоже довольно прямолинеен. std::conditional позволяет выбирать между двумя разными (произвольными) типами с помощью логического значения.

struct store_A {
    uint8_t data[];
};
struct store_B {
    uint16_t f1;
    uint16_t f2;
};
class Foo {
    constexpr static bool useVersionA = /* stuff */; 
    using store_t = std::conditional_t<useVersionA, store_A, store_B>; 
};

Здесь я использую std::conditional_t, который появляется в С++ 14, но если вы ограничены использованием С++ 11, просто сделайте:

class Foo {
    constexpr static bool useVersionA = /* stuff */; 
    using store_t = typename std::conditional<useVersionA, store_A, store_B>::type; 
};
person Alecto Irene Perez    schedule 26.04.2019