4

Is it possible to do something like:

template <unsigned majorVer, unsigned minorVer>
class Object
{
public:

    if constexpr ((majorVer == 1) && (minorVer > 10))
        bool newField;
    else
        int newInt
};

or

template <unsigned majorVer, unsigned minorVer>
class Object
{
public:

    if constexpr ((majorVer == 1) && (minorVer > 10))
        bool newField;
    // Nothing other wise
};

using c++17? I would like to change the structure of a class based on some condition that can be checked at compile time. Is there any way to achieve this?

Arun
  • 3,138
  • 4
  • 30
  • 41
  • 2
    Template specialisation. – n. m. could be an AI Apr 08 '19 at 03:35
  • 1
    This thing is possible in D language with `static if` construct but in C++ you need to play games with inheritance from `std::condtitional_t` and rely on empty base optimization. A good talk from Andrei Alexandrescu about it: https://www.youtube.com/watch?v=tcyb1lpEHm0 – Dmitry Gordon Apr 08 '19 at 09:41

2 Answers2

10

You can't use if constexpr for this. You would have to collapse them into one member using something like std::conditional:

std::conditional_t<(majorVer == 1) && (minorVer > 10), bool, int> newField;

Alternatively, you can wrap each of the two kinds of the fields in their own type:

struct A { bool newField; };
struct B { int newInt; };

And either inherit from std::conditional_t<???, A, B> or have one of those as a member.


For the case where you want either a member or nothing, the other case just needs to be an empty type. In C++20, that's:

struct E { };
[[no_unique_address]] std::conditional_t<some_condition(), bool, E> newField;

In C++17 and earlier, you'll want to inherit from this to ensure that the empty base optimization kicks in:

struct B { bool field; };
struct E { };

template <unsigned majorVer, unsigned minorVer>
class Object : private std::conditional_t<some_condition(), B, E>
{ ... };
Barry
  • 286,269
  • 29
  • 621
  • 977
2

if-constexpr is about control flow, not about memory layout. Maybe the reflection TS could be a good fit for this. However, untill that is available, you'll need other techniques.

 constexpr bool useLegacyInt(unsigned major, unsigned minor)
 {
      return (majorVer <= 1) && (minorVer <= 10));
 }

  template<bool>
  class ObjectBase
  {
        book newField;
   };

   template<>
  class ObjectBase<true>
  {
        int newInt;
   };

   template <unsigned majorVer, unsigned minorVer>
   class Object : public ObjectBase<useLegacyInt (majorVer, minorVer)>
   {};

Based on this, you could do some refinements. You don't only influence the members, also the methods. So also setters and getters ... could have a different signature. Protected helper functions could provide a bool API to Object to separate the implementation.

Finally, I would not recommend using a bool, I rather expect an enumeration as this can have multiple values.

Inheriting from an earlier version could also be possible if a new version only extends. And with some default template arguments, you can even do more fancy things.

Be warned, this kind of backwards compatibility could become complex really quickly. Sometimes it's better to just copy the complete code in a legacy version and keep it as is, without interference of the new API. This at the cost of duplicated code.

JVApen
  • 11,008
  • 5
  • 31
  • 67