18

I already know that you can enable (or not) a class's method using std::enable_if

for exemple:

template<size_t D, size_t E>
class Field
{
  ...

  size_t offset(const std::array<float,D>& p) const
  {
    ...
  }

  template<typename TT = size_t>
  typename std::enable_if<D!=E, TT>::type
  offset(const std::array<float,E>& p) const
  {
    return offset(_projection(p));
  }

  ...
};

This helps not being able to call function that are invalid in a specific case as well as removing overloading errors ... which, to me, is very nice !

I'd like to go further and make some of my class's members being present only if the are needed. That way I would get an error if I try to use an objected which would have otherwise not been initiated

I tried to do

template<size_t D, size_t E>
class Field
{
  ...

  template<typename TT = projectionFunc>
  typename std::enable_if<D!=E, TT>::type _projection;
}

But the compiler tells me :

erreur: data member ‘_projection’ cannot be a member template

Is there any way to achieve what I want ?

Amxx
  • 3,020
  • 2
  • 24
  • 45
  • 3
    You might want to change the name from `offsetof` lest someone include before your header and get the macro involved. – chris Mar 08 '15 at 19:34
  • That is planned, I'm just looking for another nice name – Amxx Mar 08 '15 at 19:40
  • That particular SFINAE is also a bit...icky. In the `D == E` case you end up with `typename std::enable_if::type`. Specializing a standard library type trait is UB, so it is arguable that a hypothetical implementation can invoke the "no valid specialization can be generated" rule and call this ill-formed. – T.C. Mar 08 '15 at 19:44

2 Answers2

11

Hold the data members in a separate class that you can then specialize as needed.

template<size_t D, size_t E>
class Field {
    template<size_t, size_t> struct Field_Members {
        int _projection;
    };
    template<size_t V> struct Field_Members<V, V> { };
    Field_Members<D, E> m;
};

and then use m._projection etc.

Field_Members doesn't have to be a nested class template; you can move it outside if desired. It is also possible to have Field inherit from it, but then it'd be a dependent base, and you'd have to write this->_projection, so it doesn't save much typing.

T.C.
  • 133,968
  • 17
  • 288
  • 421
11

AFAIK, this is not possible with a simple SFINAE inside the class template. You can, of course, have the type of the member dependent on a compile-time condition, i.e. via std::conditional, but not eliminate the member entirely.

What you can do, of course, is use another class template, such as

template<bool Condition, typename T> struct Has { T value; };
template<typename T> struct Has<false,T> {};

and declare a member (or base) of this type and access the object via Has<>::value:

template<typename T, bool Condition>
class foo
{
  Has<Condition, T> x;   // use x.value (only if Condition==true)
};
Hari
  • 1,561
  • 4
  • 17
  • 26
Walter
  • 44,150
  • 20
  • 113
  • 196