C ++ 1y模板变量用于条件变量类型和初始化

时间:2013-05-12 09:54:51

标签: c++ c++14

我有一些代码,如下所示:

// filexd.h
function() {
  // nDim is a compile time constant
  #if DIMENSIONS == 2
    static const int a[nDim] = {1,0};
  #elif DIMENSIONS == 3
    static const int a[nDim] = {2,0,1};
  #else
    #error "Unknown # of dimensions"
  #end
  // do stuff with a...
}

其他重新定义宏DIMENSIONS的文件包含此文件。我想拥有的是这样的:

static const int a[nDim] = (nDim == 2) ? {0,1} : {1,0,2};

我不想使用函数模板特化或标签调度,因为99%的代码在两种情况下都是相同的。我想保留我已经拥有的相同的函数组合,以及变量a的本地语义。所以问题是:

如何在这种情况下避免使用宏?

我可以在这里使用C ++ 1y模板变量吗?以下是否有效?

// filexd.h
function() {

  // The general case should fail somehow, maybe displaying a nice error message.
  template<int nDim> constexpr int stencil[nDim] = throw();
  // And then define the variable for both cases:
  template<> constexpr int stencil<2>[2] = {1, 0};  
  template<> constexpr int stencil<3>[3] = {2, 0, 1};

  static const stencil<nDim> a;
  for(int i = 0; i < nDim; ++i) {
    /// do stuff with a[i]
  }
}

我可以在函数本身内声明模板模板变量及其特化吗?

3 个答案:

答案 0 :(得分:5)

template<size_t N>
void the_common_work(const int(&arr)[N])
{
    for (size_t i=0; i<N; ++i)
      /// do stuff with arr[i]
}

void function()
{        
    if (nDim == 2)
    {
        int a[2] = {1,0};
        the_common_work(a);
    }
    else if (nDim == 3)
    {
        int a[3] = {2,0,1};
        the_common_work(a);
    }
}

如果你愿意,可以在那里扔static_assert

实际上,您甚至不必在此处使用模板功能。你甚至可以在C中这样做。

void the_common_work(const int * arr)
{
    for (size_t i=0; i<nDim; ++i)
        /// do stuff with arr[i]
}

另一种选择,在一个功能中:

void function()
{
    const int two[] = {1,0};
    const int three[] = {2,0,1};
    const int *arr;
    if (nDim == 2)
        arr = two;
    else if (nDim == 3)
        arr = three;
    else
    {
        throw something;
    }
    for(int i = 0; i < nDim; ++i) {
        /// do stuff with arr[i]
    }
}

答案 1 :(得分:5)

评论太长了 - C ++ 11方法出了什么问题?

#include <array>
#include <cstddef>


template < std::size_t t_dim >
constexpr std::array<int, t_dim> init()
{
    static_assert(false, "Unknown # of dimensions");
    return {};
}

  template <>
  constexpr std::array<int, 2> init<2>()
  {
      return {1, 2};
  }

  template <>
  constexpr std::array<int, 3> init<3>()
  {
      return {1, 2, 3};
  }

void function()
{
    std::array<int, nDim> a = init<nDim>();
}

int main()
{
    function();
}

编辑:使用本地类型的方法

Edit2:使用非类型模板参数包来简化声明语法。

Edit3:Simplified(在将初始化值存储为非类型模板参数后删除SFINAE)。

Edit4:结合使用第一种和上一种方法,更简单的使用语法,并允许再次作为非类型模板参数无效的类型。

警告:黑色丑陋的模板魔法

#include <array>
#include <cstddef>
#include <algorithm>
#include <iostream>
#include <iterator>

constexpr std::size_t nDim = 3;

template < typename TArr >
struct stdarray_extent
{
    static std::size_t const value = TArr{}.size();
};

template < bool t_if, typename T_Then, typename T_Else >
struct type_if
{
    using type = T_Then;
};
  template < typename T_Then, typename T_Else >
  struct type_if < false, T_Then, T_Else >
  {
      using type = T_Else;
  };

template < std::size_t t_dim, typename T, typename... TT >
struct init_
    : init_ < t_dim, TT... >
{
    using base = init_ < t_dim, TT... >;
    static bool constexpr valid = (  stdarray_extent<T>::value == t_dim
                                   ? true : base::valid);
    using type = typename type_if < stdarray_extent<T>::value == t_dim,
                                    T, typename base::type
                                  > :: type;

    template < typename TF, typename... TTF >
    static constexpr
    typename std::enable_if< stdarray_extent<TF>::value == t_dim, type >::type
    get(TF&& p, TTF&&... pp)
    {
        return p;
    }

    template < typename TF, typename... TTF >
    static constexpr
    typename std::enable_if< stdarray_extent<TF>::value != t_dim, type>::type
    get(TF&& p, TTF&&... pp)
    {
        return base::get( std::forward<TTF>(pp)... );
    }
};

  template < std::size_t t_dim, typename T >
  struct init_ < t_dim, T >
  {
      static bool constexpr valid = (stdarray_extent<T>::value == t_dim);
      using type = typename type_if < stdarray_extent<T>::value == t_dim,
                                      T,
                                      std::array<typename T::value_type, t_dim>
                                    > :: type;
      template < typename TF >
      static constexpr
      typename std::enable_if< stdarray_extent<TF>::value == t_dim, type>::type
      get(TF&& p)
      {
          return p;
      }

      template < typename TF >
      static constexpr
      typename std::enable_if< stdarray_extent<TF>::value != t_dim, type>::type
      get(TF&& p)
      {
          return {};
      }
  };

template < std::size_t t_dim, typename... TT >
auto init(TT&&... pp)
-> decltype( init_<t_dim, TT...>::get(std::forward<TT>(pp)...) )
{
    static_assert( init_<t_dim, TT...>::valid, "Unknown # of Dimensions" );
    return init_<t_dim, TT...>::get(std::forward<TT>(pp)...);
}


void function()
{
    constexpr std::size_t nDim = 3;

    std::array<int, nDim> a = init<nDim>( std::array<int,2>{{1,2}},
                                          std::array<int,3>{{3,2,1}} );

    // output
    std::copy( std::begin(a), std::end(a),
               std::ostream_iterator<int>{std::cout, ", "} );
}

int main()
{
    function();
}

答案 2 :(得分:5)

在这种情况下可能有些过分,但转换操作符可以提供一个很好的方法来实现这一点:

#include <array>
template<int i>
void f() {
    struct init {
        constexpr operator std::array<int, 2>() { return { { 1, 0 } }; }
        constexpr operator std::array<int, 3>() { return { { 2, 0, 1 } }; }
    };
    constexpr std::array<int, i> a = init{};
}

您可以使用lambda防止将名称init泄漏到函数名称空间中,但是会丢失constexpr

#include <array>
template<int i>
void f() {
    const std::array<int, i> a = [](){
        struct init {
            operator std::array<int, 2>() { return { { 1, 0 } }; }
            operator std::array<int, 3>() { return { { 2, 0, 1 } }; }
        };
        return init{};
    }();
}