代码之家  ›  专栏  ›  技术社区  ›  ilya1725

具有常数模板的静态多态性

  •  0
  • ilya1725  · 技术社区  · 4 年前

    我希望有一个静态多态性,父类有一个模板 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。

    2 回复  |  直到 4 年前
        1
  •  3
  •   AVH    4 年前

    如果你对一个额外的间接层没问题,那么你可以使用一种“traits类”的解决方案:

    #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 一你也可以这么做 Command 继承自 Message<Command> 相反,但这会使您的继承树看起来完全不同(即,具有相同消息长度的类将不再具有相同的基类)。

    当然,你可以在其中添加另一个间接层,从中继承。 MessageBase<Command> 这反过来又继承了 Message<MessageSize<Command>::size> .

        2
  •  1
  •   Swift - Friday Pie    4 年前

    这是一个常见的问题,并且有一种广泛使用的解决方案,例如在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-> 重要的是建议编译器“kCmdSize”是一个名称,具体取决于模板的参数。您需要一个依赖于模板的名称(即符合静态使用条件或在运行时使用此->)。这告诉编译器,在实例化时,这样的名称是存在的,或者将来会存在。不要忘记使用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类可能有一个公共基,但通常在使用此类结构的情况下不需要类型擦除。

        3
  •  0
  •   Serial Lazer    4 年前

    像这样的怎么样? 是否需要从主实例中传递数字,而不是让它挂在类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;
    }