я имею немного код, который выглядит так:
// 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]
}
}
Могу ли я объявить переменную шаблона трафарета с ее специализациями внутри самой функции?
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]
}
}
Слишком долго для комментария — что не так с подходом 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: использование нетипового шаблона параметров пакета для упрощения синтаксиса объявления.
Редактировать 3: Упрощено (удаление 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();
}
В этом случае это, вероятно, излишне, но операторы преобразования могут дать хороший способ сделать это:
#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
в пространство имен функции с помощью лямбды, но тогда вы потеряете 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{};
}();
}