我想在代码的性能敏感部分中使用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函数来完成工作? (更贴近我的第一个无效解决方案) 谢谢。
答案 0 :(得分:2)
答案是:您不能在C ++中使用CRTP做到这一点。发生的情况是当实例化Base<Foo>
时,Foo::bsize
还不存在。这是因为,当编译器在Base<Foo>
之前的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
的派生类型特定的版本传递给Traits
的{{1}}模板参数。
值得注意的是,您的里程可能会有所不同。特质的问题是灵活的,但问题是某个人想要实现BasicFoo
的概念时,必须确保他们实现了FooTraits
所需要的所有东西,否则他们将得到可怕的编译错误(在C ++ 20中,这可以通过concepts获得帮助)。没有仔细考虑,特质就可能成为事物的垃圾场,这使得实施替代BasicFoo
变得更加困难。