【问题标题】:Static polymorphism with constant template具有常量模板的静态多态性
【发布时间】:2020-11-02 08:22:18
【问题描述】:

我想要一个静态多态性,父类的模板大小为std::array。此代码工作正常:

#include <iostream>
#include <array>

using namespace std;

template <size_t size>
class Message
{
  public:
    size_t GetSize() { return size; }
  private:
    std::array<uint8_t, size> data_{};
};

class Command : public Message<12>
{
  public:
    static const size_t kCmdSize{12};
  private:
};

class Reply : public Message<16>
{
  public:
    static const size_t kCmdSize{12};
  private:
};

int main()
{
    Command cmd{};
    Reply rpl{};
    cout << "Size: " << cmd.GetSize() << "|" << rpl.GetSize() << endl;
    return 0;
}

但我不是幻数的忠实粉丝。

有没有办法使用子类中声明的常量作为父类的参数?类似的东西:

class Command : public Message<kCmdSize>
{
  public:
    static const size_t kCmdSize{12};
  private:
};

直接使用它会尝试使用尚不存在的类中的变量。 使用 C++14。

【问题讨论】:

  • 标准方法是使用 CRTP 和 trait 将常量从派生类传播到基类。

标签: c++ c++11 c++14


【解决方案1】:

如果您对额外的间接层没问题,那么您可以使用“特征类”类型的解决方案:

#include <array>
#include <cstdint>

template <class T> struct MessageSize;

template <std::size_t size>
class Message {
  public:
    std::size_t GetSize() { return size; }

  private:
    std::array<std::uint8_t, size> data_{};
};

// Forward declare class for upcoming specialization.
class Command;

// Specialize message size for Command class.
template <> struct MessageSize<Command> {
  static constexpr std::size_t size = 12;
};

class Command : public Message<MessageSize<Command>::size> { };

请注意,在我的示例中,Message 类本身不使用 MessageSize 类。您也可以这样做,并改为让CommandMessage&lt;Command&gt; 继承,但这会使您的继承树看起来完全不同(即具有相同消息长度的类将不再具有相同的基类)。

您当然可以在其中添加另一层间接,您可以在其中继承例如MessageBase&lt;Command&gt; 又继承自 Message&lt;MessageSize&lt;Command&gt;::size&gt;

【讨论】:

  • @Swift-FridayPie 他们确实做到了。所有消息长度为 12 的类都将继承自 Message&lt;12&gt;。如果他们从Message&lt;X&gt;Message&lt;Y&gt;、...继承,他们不会
  • 啊,我发现了最后一段,这正是我想到的。这是 CRTP 的一种扩展和懒惰的情况,Message&lt;MessageSize&lt;Command&gt;::size&gt; 是一个拗口,但可以通过专门化来隐藏它。
  • @Swift-FridayPie 是的,但是你会得到一个依赖基础,这将需要使用例如this-&gt; 在代码中。所以我想如果 OP 可以忍受使用 MessageSize 辅助结构显式继承,那为什么不呢。
  • 是的,那是纯粹的风格。但是 12 年来,我与项目负责人打交道,他们讨厌看到这种继承,如果他们看到了就放弃整个设计。 “我看不懂!”
  • 是的,但是我有一些项目,在这些依赖的基类中有很多功能,而且必须输入数百次 this-&gt; 会很烦人 :)。但是,嘿,如果这就是设计所需要的......
【解决方案2】:

这是常见问题,并且有广泛使用的解决方案,例如在 C++ 库的流组件的实现中。与具体派生类相关的类型定义和常量成为 trait 类特化的一部分:

template <class T> 
struct CommandTrait;

template <class T> 
struct Message : public CommandTrait<T>
{
    constexpr size_t GetSize() { return this->kCmdSize; }  // or Message::kCmdSize, the same in this case

    std::array<std::uint8_t, Message::size> data_{};
};

template <size_t _Sz, size_t _CSc = 12 > 
struct MessageSize  {
  static constexpr std::size_t size = _Sz;
  static constexpr size_t kCmdSize{ _CSc };
};

template <> 
struct CommandTrait<struct Command> : MessageSize<12> {};

template <> 
struct CommandTrait<class Reply> : MessageSize<16> {};

class Command : public Message<Command>
{
};

class Reply : public Message<Reply>
{
};

请注意,this-&gt; 建议编译器“kCmdSize”是一个取决于模板参数的名称,这一点很重要。您需要一个依赖于模板的名称(即有资格用于静态使用或使用 this-> 用于运行时)。这告诉编译器在实例化时期望这样的名称存在或将来会存在。不要忘记使用 constexpr 的可能性:

template <class T> 
struct Message : public CommandTrait<T>
{
    static constexpr size_t GetSize() { return Message::kCmdSize; }
    
    std::array<std::uint8_t, GetSize()> data_{};
};

Trait 类可能有一个共同的基础,但通常在使用此类结构时不需要类型擦除。

【讨论】:

  • 抱歉,这行不通。我需要size 才能声明std::array 成员变量。
  • @ilya1725 它会:std::array&lt;std::uint8_t, Message::kCmdSize&gt; data_{}; 您需要一个依赖于模板的名称(即有资格用于静态使用或使用 this-> 用于运行时)。这告诉编译器在实例化时期望这样的名称存在或将来会存在
  • 是的,它适用于一个儿童班级。但是,我必须更新问题以更好地反映实际问题。
【解决方案3】:

这样的事情怎么样? 是否担心从主实例传递数字而不是让它在类 impl 中挂起。

#include <iostream>
#include <array>

using namespace std;

template <size_t size>
class Message
{
  public:
    size_t GetSize() { return size; }
  private:
    std::array<uint8_t, size> data_{};
};

template<size_t N>
class Command : public Message<N>
{
  public:
    static const size_t kCmdSize{N};
  private:
};

int main()
{
    Command<12> cmd{};
    cout << "Size: " << cmd.GetSize() << endl;
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-28
    • 1970-01-01
    • 2012-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-05
    • 1970-01-01
    相关资源
    最近更新 更多