具有自定义初始化的 C++ 静态分派

C++ static dispatch with custom initialization

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

template <typename Derived>
class Base {
protected:
    std::bitset<Derived::bsize> data_;
};

class Foo : public Base<Foo> {
public:
    constexpr static size_t bsize = 2;
};

但编译器抱怨:"no member bsize in Foo"。我想我可以通过在基础 class:

中模板化位集长度来解决我的问题
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 class 上看到 Base<Foo> 时,就会发生这种情况。没相当这么简单,但这是一般的想法。

这里有一个解决方法可以满足您的需求,即将所有必要的信息捆绑在 class 中,然后将其作为模板参数提供。我不知道这种模式的名称(我见过 "baggage class," 但感觉带有贬义),但您可以在标准库中找到这种模式的示例,例如 std::char_traits.

class FooTraits {
    constexpr static size_t bsize = 2;
};

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

Traits 非常灵活——您只需将 static constexpr 函数放入 FooTraits 即可计算您需要的任何内容。在您的情况下,派生 class 会将 FooTraits 的派生类型特定版本传递给 BasicFoo.

Traits 模板参数

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