0

I'm pretty new to C++ template programming. I'd like to design a function element such as e.g.

  1. element<3, 3, 3, 3, 3> will return 3
  2. element<3, 3, 2> will fail an assertion

    #include <iostream>
    #include <cstdlib>
    
    namespace meta
    {       
      template<typename T>
      constexpr T element(T x)
      {
        return x;
      }
    
      template<typename T, typename... Ts>
      constexpr T element(T x, Ts... xs)
      {
        constexpr T oth = element(xs...);             // $C$
        static_assert(oth == x, "element Mismatch");
        return x;
      }
    
      template<int... DIMS>
      void get_elements()
      {
        std::cout << "elements " << element(DIMS...); // $A$
      }
    }
    
    int main(int argc, char ** argv)
    {
      meta::get_elements<2, 3, 4>();                  // $B$
    
      static constexpr int D1 = 3, D2 = 3;
    
      meta::get_elements<D1, D2>();
    }
    

But GCC with std=c++14 is failing with

In instantiation of ‘constexpr T meta::element(T, Ts ...) [with T = int; Ts = {int, int}]’:

$A$: required from ‘void meta::get_elements() [with int ...DIMS = {2, 3, 4}]’

$B$: required from here

$C$: error: ‘xs#0’ is not a constant expression

$C$: error: ‘xs#1’ is not a constant expression

I'd like to exploit recursion to perform an equality check on each template argument in the list, and return one of them if they're all equal.

Patrizio Bertoni
  • 2,582
  • 31
  • 43

3 Answers3

2

Here is a simple solution in C++17:

template <int Head, int... Tail> struct element
{
    static_assert((... && (Head == Tail)), "missmatch elements");
    static constexpr auto value = Head;
};

template <int... I> constexpr auto element_v = element<I...>::value;

auto test()
{
    // element_v<3, 3, 1>; // assert fail

    constexpr int A = 3, B = 3;
    return element_v<3, 3, A, B>;
}

See it on godbolt

bolov
  • 72,283
  • 15
  • 145
  • 224
2

Or pre-c++17 (no recursion):

#include <iostream>
#include <cstdlib>
#include <type_traits>


namespace meta
{     

  template <bool...>
  struct boolpack { };

  template <bool... Bs>
  struct all_of: std::is_same<boolpack<Bs..., true>, boolpack<true, Bs...>> { };


  template<int FIRST, int... DIMS>
  constexpr void get_elements()
  {
      static_assert(all_of<DIMS==FIRST...>::value, "!");
      std::cout << "elements " << FIRST;
  }
}

int main(int argc, char ** argv)
{
  static constexpr int D1 = 3, D2 = 3;

  meta::get_elements<D1, D2>();
  //meta::get_elements<D1, D2, 2>(); //fail
}

[see it live]

Edited: all_of inspired by Jarod42.

W.F.
  • 13,888
  • 2
  • 34
  • 81
2

parameters are not constexpr.

You might use std::integral_constant to bypass that

namespace meta
{       
  template<typename T, T V>
  constexpr std::integral_constant<T, V> element(std::integral_constant<T, V>)
  {
    return {};
  }

  template<typename T, T V, typename... Ts>
  constexpr std::integral_constant<T, V> element(std::integral_constant<T, V> x, Ts... xs)
  {
    constexpr auto oth = element(xs...);             // $C$
    static_assert(oth() == x(), "element Mismatch");
    return {};
  }

  template<int... DIMS>
  void get_elements()
  {
    std::cout << "elements " << element(std::integral_constant<int, DIMS>{}...); // $A$
  }
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302