如何在编译时静态生成浮点数据
How do you statically generate float data at compile time?
鉴于我想对某些数据执行过滤,我如何避免在运行时生成此数据,但保持更改这些过滤器的大小和数据分布的灵活性,同时保持干净可重用的代码。我知道我可以使用模板执行以下操作:
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);
这确实会根据 someDistribution(...) 根据过滤器对象中的索引 x 在我的过滤器中生成数据。但是我的使用有一些缺点...
1)我认为我说得对,虽然这些数据不是在对象构造时生成的,但它是在程序启动时生成的。- 我可以容忍这一点,尽管宁愿在 comiletime 计算过滤器并在那时和那里烘焙(这甚至可能用于浮点数据吗?
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
其值),而编译时常量的值始终是已知的。因此,前者可能有点慢,但可能没有任何意义。在引入不便之前,请务必先测量。
-
再说一遍,你为什么关心?如果特定
x
的类型没有在代码中的任何位置使用Filter
,为什么该值应该在某个地方?如果模板静态相互依赖,它们就会出现问题 - 在您的情况下,它们不是,每个
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
改成一个函数,即使是内联函数,你会看到计算必须进行。
编辑:请记住,你可以用宏做任何事情,包括"专门化"它们的某些值。简单分发应该是预处理器友好的。
您可以使用可变参数模板获得拼图的一部分。一旦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;
}
为了使这个编译时而不是在启动时初始化,尽管你真的想要VS 2013没有的constexpr支持。这是 constexpr 版本:
#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;
}
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 静态数据成员模板专用化的实例化点在哪里
- 内联静态数据的初始化
- 调用在 HXX 文件中声明的静态数据成员
- 虚拟成员函数的定义是否强制在同一转换单元中动态初始化静态数据成员?
- 错误: 无效使用非静态数据成员"应用程序::应用程序构造函数"
- 静态数据成员:它"const declaration / constexpr definition"起作用?
- 关于静态常量数据模因的声明和定义的混淆
- 何时需要定义类的静态数据成员 (un/-)
- 为什么类成员数据必须是静态的才能被模板化类的模板化结构成员访问
- 为什么静态数据成员不能在c++11中的类中初始化
- 访问模板化类的非模板基的静态数据
- 静态数据成员的模板专用化
- 拒绝包含某些公共静态数据成员的类型
- GCC:在调试构建中优化的静态数据成员
- 类模板静态数据成员定义/声明/初始化
- 是否允许在作为静态数据结构成员的lambda函数中捕获变量
- C++ 中的静态数据成员
- 如果类在 C++ 中具有常量或引用类型的非静态数据成员,为什么编译器不提供默认赋值运算符?
- 使用 lambda 函数初始化静态数据成员