6

鉴于我想对某些数据执行过滤,我怎样才能避免在运行时生成这些数据,但要保持更改这些过滤器的大小和数据分布的灵活性,同时还要保持干净整洁的可重用代码。我知道我可以使用模板来执行以下操作:

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 预编译脚本来生成包含过滤器数据的源文件,但我不想在这里使用它,因为那是它自己的鱼锅!)

4

2 回答 2

2

既然你不能使用contexpr......关于你的问题:

  1. 除非您正在寻找下一个最大的素数,否则您不应该担心在启动时会进行一次简单的计算。尝试测量它,您可能会发现初始化在不到一毫秒的时间内完成。

    也就是说,在启动时计算的值表现为变量(asked每次使用时都必须为它们的值),而编译时常量的值总是已知的。因此前者可能会慢一些,但可能没有任何意义。

    在引入不便之前,请务必先进行测量。

  2. 再说一次,你为什么在乎?如果Filtertype for specificx没有在代码中的任何地方使用,为什么该值应该在某个地方?

    如果它们相互依赖,模板静态是有问题的——在你的情况下,它们不是,每个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;
}

程序集(Clang):

main:                                   # @main
    movl    $50000, %eax            # imm = 0xC350
    ret

如果你变成someDistribution一个函数,即使是内联函数,你会发现必须进行计算。

编辑:记住,你可以用宏做任何事情,包括“专门化”它们以获得某些值。简单的分发应该是预处理器友好的。

于 2013-11-01T15:58:36.807 回答
-1

您可以使用可变参数模板获得难题的一部分。一旦将 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;
}
于 2013-11-01T15:57:27.040 回答