Учитывая, что я хочу выполнить фильтрацию некоторых данных, как можно избежать генерации этих данных во время выполнения, но при этом сохранить гибкость в изменении размеров и распределения данных этих фильтров, сохраняя при этом хороший чистый повторно используемый код. Я знаю, что могу использовать шаблоны, чтобы сделать что-то вроде следующего:
template <int x> class Filter
{
static const float f;
static const Filter<x-1> next;
inline float* begin(const Filter<x>& el){ return &f; }
inline float* end(const Filter<x>& el) { return (&f)+x+1; }
};
template <> class Filter<0>
{
static const float f;
inline float* begin(const Filter<0>& el){ return &f; }
inline float* end(const Filter<0>& el) { return (&f)+1; }
};
template <int x> const float Filter<x>::f = someDistribution(x);
template <> const float Filter<0>::f = someDistribution(0);
Это действительно сгенерирует данные в моем фильтре в соответствии с индексом x в объекте фильтра в соответствии с someDistribution (…). Однако есть некоторые недостатки для моего использования …
1) Я думаю, что я прав, говоря, что, хотя эти данные не генерируются при построении объекта, они генерируются один раз при запуске программы. — это я мог бы терпеть, хотя скорее фильтр будет вычисляться во время компиляции и запекаться тут же (возможно ли это даже для данных с плавающей запятой?)
2) Фильтр не будет создавать экземпляр «следующего» члена, если не существует функции-члена (которая вызывается где-то!), Которая пересекает длину структуры, т.е.
// inside template<int x> class Filter
inline void instantiate() const { next.instantiate(); };
// then inside template<> class Filter<0>
inline void instantiate() const { };
Я, должно быть, делаю это неправильно, чтобы требовать функции создания экземпляров, и это не соответствует легко поддерживаемому предложению.
редактировать: причина, по которой я забочусь здесь, заключается в том, что я хотел бы убедиться, что next
элементы создаются, так что я могу обойти статический «массив», используя функции начала и конца.
Итак, во-первых, как мне исправить проблему 2 и покончить с функцией экземпляра, а во-вторых, можно ли исправить проблему 1, чтобы эти данные динамически генерировались во время компиляции и создавались обратно.
(Обратите внимание, что по схожим проблемам я использовал сценарии предварительной компиляции Python для генерации исходных файлов, содержащих данные фильтра, но я не хочу использовать это здесь, так как это собственный чайник рыбы!)
Так как вы не можете использовать contexpr
… По поводу ваших вопросов:
Если вы не ищете следующее наибольшее простое число, вас не должно беспокоить, что простые вычисления выполняются один раз при запуске. Попробуйте измерить его, и, вероятно, вы обнаружите, что инициализация выполняется менее чем за миллисекунду.
Тем не менее, значения, вычисленные при запуске, ведут себя как переменные (должны быть asked
для их значения каждый раз, когда они используются), тогда как значение константы времени компиляции всегда известно. Следовательно, первый может быть немного медленнее, но, вероятно, без какого-либо значения.
Всегда измеряйте сначала, прежде чем вносить неудобства.
Опять же, почему тебя это волнует? Если Filter
тип для конкретного x
нигде в коде не используется, почему значение должно быть где-то там?
Шаблонные статики проблематичны, если они зависят друг от друга — в вашем случае они не каждый f
автономен
Сказав все это, отличный инструмент, чтобы возиться вокруг http://gcc.godbolt.org/ — вы видите сборку, когда вы печатаете. Он НЕ поддерживает компиляторы MS, но дает вам довольно хорошее предположение о том, как компиляторы оптимизируют вещи.
Если ваш дистрибутив достаточно прост, чтобы быть макросом, это будет константа времени компиляции:
#define someDistribution(x) x * x
template <int x> struct Filter
{
static const float f;
};
template <int x> const float Filter<x>::f = someDistribution(x);
int main()
{
return Filter<200>::f + Filter<100>::f;
}
Сборка (лязг):
main: # @main
movl $50000, %eax # imm = 0xC350
ret
Если вы измените someDistribution
чтобы быть функцией, даже встроенной, вы увидите, что вычисления должны произойти.
РЕДАКТИРОВАТЬ: помните, что вы можете делать maaany вещи с макросами, в том числе «специализируя» их для определенных значений. Простое распространение должно быть дружественным к препроцессору.
Вы можете получить одну часть головоломки, используя различные шаблоны. Как только поддержка integer_sequence добавлена в стандартную библиотеку, вы можете использовать ее вместо seq / gen_seq.
#include <array>
#include <iostream>
using namespace std;
template<size_t... Is> struct seq {};
template<size_t N, size_t... Is> struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};
template <size_t... Is> struct gen_seq<0, Is...> : seq<Is...>{};
template<typename Func, size_t... Is>
const array<float, sizeof...(Is)>& make_coeffs(Func f, seq<Is...>) {
static const auto coeffs = array<float, sizeof...(Is)>{ f(Is)... };
return coeffs;
}
float square(float x) { return x * x; }
int main() {
const auto coeffs = make_coeffs(square, gen_seq<10>{});
for (float x : coeffs) {
cout << x << " ";
}
cout << endl;
}
Чтобы сделать это время компиляции, а не инициализировать при запуске, хотя вам действительно нужна поддержка constexpr, которой нет в VS 2013. Вот констекстр версия:
#include <array>
#include <iostream>
using namespace std;
template<size_t... Is> struct seq {};
template<size_t N, size_t... Is> struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};
template <size_t... Is> struct gen_seq<0, Is...> : seq<Is...>{};
template<typename Func, size_t... Is>
constexpr array<float, sizeof...(Is)> make_coeffs(Func f, seq<Is...>) {
return array<float, sizeof...(Is)>{ f(Is)... };
}
constexpr float square(float x) { return x * x; }
int main() {
constexpr auto coeffs = make_coeffs(square, gen_seq<10>{});
static_assert(coeffs[3] == 9, "");
for (float x : coeffs) {
cout << x << " ";
}
cout << endl;
}