递归模板参数包编程

时间:2018-04-30 08:37:34

标签: c++ variadic-templates

我对C ++模板编程很陌生。我想设计一个函数element,例如,例如

  1. element<3, 3, 3, 3, 3>将返回3
  2. element<3, 3, 2>将失败一个断言

    #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>();
    }
    
  3. 但是std=c++14的GCC失败了

      

    在实例化'constexpr T meta :: element(T,Ts ...)[用T = int; Ts = {int,int}]':

         

    $ A $:来自'void meta :: get_elements()[with int ... DIMS = {2,3,4}]'

         

    $ B $:从这里需要

         

    $ C $:错误:'xs#0'不是常量表达式

         

    $ C $:错误:'xs#1'不是常量表达式

    我想利用递归对列表中的每个模板参数执行相等检查,如果它们全部相等则返回其中一个。

3 个答案:

答案 0 :(得分:2)

这是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>;
}

godbolt

上查看

答案 1 :(得分:2)

或pre-c ++ 17(无递归):

#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]

已编辑all_of受到Jarod42的启发。

答案 2 :(得分:2)

参数不是constexpr

您可以使用std::integral_constant来绕过

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$
  }
}