3

我有一些看起来像这样的代码:

// 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]
  }
}

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

4

3 回答 3

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]
    }
}
于 2013-05-12T10:51:04.517 回答
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:简化(将初始化值存储为非类型模板参数后可以删除 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();
}
于 2013-05-12T12:22:28.183 回答
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{};
}

init您可以使用 lambda防止将名称泄漏到函数命名空间中,但随后您会丢失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{};
    }();
}
于 2013-05-16T07:58:20.170 回答