我有一些代码,如下所示:
// 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]
}
}
我可以在函数本身内声明模板模板变量及其特化吗?
答案 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{};
}();
}