Using enable_if to optionally add a struct member
Asked Answered
A

4

11

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.

Agnostic answered 13/4, 2012 at 11:48 Comment(0)
P
5

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.

Pichardo answered 13/4, 2012 at 11:57 Comment(2)
if_ is usually called std::conditional.Pacify
@KerrekSB: Ah thanks, I am somewhat of an old-timer I am afraid. In Boost MPL it was if_ :) I haven't dug much in the C++11 new libraries yet :/Pichardo
R
5

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
};
Richellericher answered 13/4, 2012 at 11:56 Comment(2)
That would cause repetition of all the common fields and member functions - I want to avoid this (DRY).Agnostic
@Agnostic - use composition or inheritance and do what I suggested on a member or a base classRichellericher
P
5

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.

Pichardo answered 13/4, 2012 at 11:57 Comment(2)
if_ is usually called std::conditional.Pacify
@KerrekSB: Ah thanks, I am somewhat of an old-timer I am afraid. In Boost MPL it was if_ :) I haven't dug much in the C++11 new libraries yet :/Pichardo
T
2

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;
                    };
Transpontine answered 13/4, 2012 at 12:18 Comment(2)
That is the closest one to what I need - but surely this is much more verbose than a single line of "enable_if"...Agnostic
If verbose doesn't work for you then you should at least take a hard look at your choice of using the C++ language. It can be very verbose at times.Superfine
J
2

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.

Joannjoanna answered 13/7, 2020 at 19:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.