C++具有自定义初始化的静态调度

C++ static dispatch with custom initialization

本文关键字:静态调度 初始化 自定义 C++      更新时间:2023-10-16

我想将CRTP用于代码中对性能敏感的部分。但是,我的基类有一个位集,其大小取决于派生类。我希望这样的事情会起作用:

template <typename Derived>
class Base {
protected:
std::bitset<Derived::bsize> data_;
};
class Foo : public Base<Foo> {
public:
constexpr static size_t bsize = 2;
};

但编译器抱怨:"Foo中没有成员bsize"。我想我可以通过在基类中模板化位集长度来解决我的问题:

template <typename Derived, size_t size>
class Base {
protected:
std::bitset<size> data_;
};
class Foo : public Base<Foo,2> { ... };

将来,我可能希望使用更复杂的表达式来计算位集长度。有没有办法使用 constexpr 函数完成工作?(在精神上更接近我的第一个非工作解决方案) 谢谢。

答案是:你不能在C++中使用CRTP执行此操作。正在发生的事情是,当Base<Foo>被实例化时,Foo::bsize还不存在。这是因为当编译器在Foo类上看到Base<Foo>时,就会发生这种情况,该类位于您的{之前。事情并没有那么简单,但这是一般的想法。

这里有一种解决方法可以执行您想要的操作,即将所有必要的信息捆绑在一个类中,然后将其作为模板参数提供。我不知道这个模式的名称(我见过"行李类",但这感觉很贬义),但你可以在标准库中找到这样的例子,比如std::char_traits

class FooTraits {
constexpr static size_t bsize = 2;
};
template <class Traits = FooTraits>
class BasicFoo {
protected:
std::bitset<Traits::bsize> data_;
};

特征非常灵活 - 您可以将static constexpr函数放入FooTraits中以计算您需要的任何内容。在您的情况下,派生类会将派生类型特定的FooTraits版本传递给BasicFooTraits模板参数。

值得注意的是,您的里程可能会有所不同。虽然灵活,但特征的问题在于,想要实现FooTraits概念的人需要确保他们实现BasicFoo需要的所有东西,否则他们将得到一个可怕的编译错误(在 C++20 中,这是通过概念帮助的)。如果不仔细考虑,特质可能会成为事物的倾倒场,这使得实施替代方案FooTraits变得更加困难。