2

I want to do something like this:

template <uint64_t N>
struct a {
  static constexpr T1 v1 = {};
  static constexpr T2 v2 = {};
  static constexpr auto v3 = (N % 2 == 1 ? v1 : v2);
};

But I can't use (? :) with different types. How I can do this?

dasfex
  • 1,148
  • 2
  • 12
  • 30
  • 1
    Does this answer your question? [Something like "if constexpr" but for class definition](https://stackoverflow.com/questions/41118861/something-like-if-constexpr-but-for-class-definition) – Alexey S. Larionov Aug 29 '20 at 11:26
  • Look up template specialisation (or, US spelling, specialization) – Peter Aug 29 '20 at 11:28
  • @AlexLarionov, can you add some info about the way of using this question? – dasfex Aug 29 '20 at 11:32

2 Answers2

6

For example, you can use if constexpr and a lambda function (C++17):

template <std::uint64_t N>
struct a {
  static constexpr T1 v1 = {};
  static constexpr T2 v2 = {};
  static constexpr auto v3 = 
    [] {
        if constexpr (N % 2 == 1) 
            return v1;
        else
            return v2;
    }();
};

Another solution with a std::tuple (C++14):

template <std::uint64_t N>
struct a {
  static constexpr T1 v1 = {};
  static constexpr T2 v2 = {};
  static constexpr auto v3 = std::get<N % 2>(std::make_tuple(v2, v1));
};
Evg
  • 25,259
  • 5
  • 41
  • 83
  • 3
    Very nice the `std::get()` solution: works also in C++11/C++14. – max66 Aug 29 '20 at 11:45
  • 1
    @max66 the `std::get` solution only works at compile time in C++14. C++11 doesn't define `std::get` to be `constexpr` – Human-Compiler Aug 29 '20 at 12:46
  • @Human-Compiler - D'Oh! You're right: `constexpr` from C++14. – max66 Aug 29 '20 at 14:48
  • @max66 Curiously, with GCC and Clang it works even with `-std=c++11`. Probably, they have `constexpr` for these functions in C++11 already. – Evg Aug 29 '20 at 15:34
  • @Evg - Well... from clang I get a compiler error... but I confirm from gcc.... the bad part is that works also adding "`-pedantic`"... I think we should consider this a gcc bug (in case of pedantic). – max66 Aug 29 '20 at 17:36
3

If you can use C++17 (but you tagged C++11 and C++14 only) a if constexpr solution based (very elegant the lambda based from Evg) is preferable, IMHO.

Before C++17, I suppose you can try using SFINAE. By example

#include <type_traits>

template <int N>
struct Foo
 {
   static constexpr int v1 = {};
   static constexpr long v2 = {};

   template <int M = N>
   constexpr static std::enable_if_t<M % 2 == 1, int> getV3 ()
    { return v1; }

   template <int M = N>
   constexpr static std::enable_if_t<M % 2 != 1, long> getV3 ()
    { return v2; }


   static constexpr auto v3 = getV3(); 
 };

int main ()
 {
   static_assert( std::is_same_v<int const, decltype(Foo<1>::v3)> );
   static_assert( std::is_same_v<long const, decltype(Foo<2>::v3)> );
 }

As suggested by Evg (thanks!) you can avoid SFINAE using overloading (good-old tag dispatching). By example

template <int N>
struct Foo
 {
   static constexpr int v1 = {};
   static constexpr long v2 = {};

   constexpr static auto getV3 (std::true_type)
    { return v1; }

   constexpr static auto getV3 (std::false_type)
    { return v2; }

   static constexpr auto v3 = getV3(std::integral_constant<bool, N%2>{}); 
 };
max66
  • 65,235
  • 10
  • 71
  • 111
  • 4
    Instead of SFINAE, one can also use overload resolution for `std::integral_constant{}`. – Evg Aug 29 '20 at 12:06