Say I have the following templated class:
template<typename T>
class Foo {
struct store_t {
uint8_t data[];
} store;
/// other stuff using T
}
Is there a way to construct a specialized version of the internal struct that would equate to something like this:
class Foo {
struct store_t {
uint16_t f1;
uint16_t f2;
} store;
/// other stuff using T
}
I would prefer to keep most of the "other stuff using T" unspecialized. I would specialize some accessors though. I feel I would want to write something like
template<>
struct store_t {
uint16_t f1;
uint16_t f2;
} Foo<someT>::store;
but that of course doesn't work.
As with most things in life, the answer to "how do I solve this problem I'm having with templates" is "use more templates."
store_t
as a templateThankfully, we don't have to do anything crazy. Let's write store_t
outside of Foo
as a template:
template<bool use_uint8>
struct Foo_store_t {
uint8_t data[];
};
template<>
struct Foo_store_t<false> {
uint16_t f1;
uint16_t f2;
};
Now, when writing Foo
, we can just pick which one we wanna use by testing some condition:
template<class T>
class Foo {
constexpr static bool use_uint8 = /* stuff */;
using store_t = Foo_store_t<use_uint8>;
store_t store;
};
store_t
, use std::conditional
This one is also pretty straight-forward. std::conditional
lets you pick between two different (arbitrary) types using a boolean.
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>;
};
Here I'm using std::conditional_t
, which appears in C++14, but if you're restricted to using C++11 just do:
class Foo {
constexpr static bool useVersionA = /* stuff */;
using store_t = typename std::conditional<useVersionA, store_A, store_B>::type;
};
Just for fun, I show another possible solution based over a sort of self-inheritance.
Suppose you want specialize Foo
for the type bool
.
You can write the main Foo
adding a template non-type parameter with a default value (say a bool
with a default value of true
)
template <typename T, bool = true>
struct Foo
{
struct store_t
{ std::uint8_t data[10]; } store;
/// other stuff using T
T value;
};
I've added T value
as example of "other stuff using T".
Now you can specialize Foo<bool>
inheriting from Foo<bool, false>
template <>
struct Foo<bool> : public Foo<bool, false>
{
struct store_t
{ std::uint16_t f1, f2; } store;
};
This way you can specialize store_t
/store
(and other members, if you want) inheriting from Foo<bool, false>
the "other stuff using T" (a bool value
, by example).
The following is a full compiling example
#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, "!");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With