Using enable_if to optionally add a struct member
-
31-05-2021 - |
Question
Given this template:
template <class A>
struct Something {
... // members common to all template instantiations for all A types
SpecialType member; // but not this - I want this to be conditional...
}
...I want to use "enable_if" to have the SpecialType member exist conditionally; that is, only when the template is instantiated with A=SpecialCase1 or SpecialCase2 types. In all other cases, I want the SpecialType member to be missing.
In case you're wondering why, this is about optimization - i.e. not carrying useless payload in the struct. I am a newbie in template metaprogramming, but I understand I need "enable_if" and two "is_same" somehow - not sure exactly how, though...
EDIT: Doing it with generic C++ (i.e. without Boost-specifics) would be a plus.
Solution
Well: use a base class.
struct Empty {};
struct SpecialTypeCnt { SpecialType member; };
template <typename A>
struct Something: if_< /* cond */ , SpecialTypeCnt, Empty>::type {
};
Where if_
is defined as:
template <typename, typename, typename E> struct if_ { typedef E type; };
template <typename T, typename E>
struct if_<std::true_type, T, E> { typedef T type; };
(You can also specialize on a boolean)
Now of course, you need to express your condition properly.
Having said that, you should probably not use just a struct
. Instead you should use a class
which provides the operations that need be applied on member
. Then you provide a class Null
with a default behavior and a class SomeType
with the behavior specific to member
.
Otherwise you'll rewrite the condition everywhere you need to "perhaps" modify member
, and it gets annoying real quick.
OTHER TIPS
You don't need enable_if for this. Specialize your struct for special cases and leave the default implementation for the rest:
template <class A>
struct Something
{
// your default implementation
};
template <>
struct Something<SpecialCase1>
{
// your SpecialCase1 implementation
};
template <>
struct Something<SpecialCase2>
{
// your SpecialCase2 implementation
};
In order not to duplicate common members:
Define BaseSomething class:
template <class A>
struct BaseSomething {
... // members common to all template instantiations for all A types
};
Define SpecialSomething class:
template <class A>
struct SpecialSomething {
SpecialType member;
...//SpetialType related functionality
};
Define Something class:
template <class A>
struct Something :public BaseSomething<A>{
};
template<>
struct Something<SpecialCase1>:public BaseSomething<A>{
SpecialSomething<SpecialCase1> special;
};
template<>
struct Something<SpecialCase2>:public BaseSomething<A>{
SpecialSomething<SpecialCase2> special;
};
This has already been answered by Matthieu M. However, a slightly more idoimatic and elegant solution would be to do the following:
struct OptionalMembers { SpecialType member; };
template <typename T>
class Something: public conditional_t<is_same<T, SpecialCase>, OptionalMembers, tuple<>> {
};
This example shows how to add optional members only when template parameter is of type SpecialCase.