Как специализировать структуру члена шаблонного класса - PullRequest
4 голосов
/ 26 апреля 2019

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

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
}

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

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

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

Ответы [ 2 ]

6 голосов
/ 26 апреля 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, который появляется в C ++ 14, но если вы ограничены использованием C ++ 11, просто сделайте:

class Foo {
    constexpr static bool useVersionA = /* stuff */; 
    using store_t = typename std::conditional<useVersionA, store_A, store_B>::type; 
};
2 голосов
/ 26 апреля 2019

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

Предположим, вы хотите специализироваться 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" (a 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, "!");
 }
...