【问题标题】:C++ Template-styled function calling? [closed]C ++模板样式的函数调用? [关闭]
【发布时间】:2016-05-02 15:53:11
【问题描述】:

我希望我的代码干净,因此我想实现这样的目标:

Set<Color>(255, 255, 255);
Set<Opacity>(128);
Draw<Rectangle>(0, 0, 50, 50);
Set<Opacity>(255);
Draw<Texture>(Textures::Woman, 75, 75);

对于放在.

之间的不同名称,基本上有多种形式的函数

如果它不是一个实际的模板,那就太好了,这将需要我在 .h 文件中编写所有内容,但我觉得不干净。

【问题讨论】:

  • 你不能在没有模板的情况下使用&lt;&gt; 语法,所以它不可能在没有模板的情况下使用它。请注意,您可以使用显式模板实例化,因此您不需要头文件中的所有代码。
  • 你可以在 .cpp 文件中实现模板主体,然后将其包含到标题中
  • 您真正想用这种技术解决什么问题?使用 mixin 基础进行属性实现?您应该详细说明您的用例并展示一个完整的(伪)代码示例。
  • 它不会为您的 API 的用户保存任何输入,为什么?

标签: c++ function void


【解决方案1】:

如何使用函数重载。

Set(Color(255, 255, 255));
Set(Opacity(128));
Draw(Rectangle(0, 0, 50, 50));
Set(Opacity(255));
Draw(Texture(Textures::Woman, 75, 75));

【讨论】:

    【解决方案2】:

    为什么不直接重载函数(假设签名每次都不同,在你的情况下就是这样)?

    //set color
    void Set(int r, int g, int b) {}
    
    //set opacity
    void Set(int op) {}
    
    //draw rectangle
    void Draw(int x, int y, int w, int h) {}
    
    //draw image
    void Draw(Texture tex, int w, int h) {}
    

    注意:制作单独的函数(SetColorSetOpacity)会使代码比那些彼此无关的重载更清晰,因此您应该考虑改用它。

    【讨论】:

    • 示例代码的缺点是,这样的接口没有在调用方提供任何语义信息。 Set(42, 17, 32); 到底设置了什么?
    • @cdonat true,但是您可以解释参数(rgb -> 必须是颜色)。但我同意,这不是最好的方法。
    • 在调用方,当您阅读代码时,您看不到参数名称。
    • @cdonat 仅在您使用足够好的 IDE 时使用,但如果您没有使用,请坚持 Loki 的答案或我的替代方案。
    【解决方案3】:

    另一种方法是使用标签:

    //set color
    void Set(Color_tag, int r, int g, int b) {}
    
    //set opacity
    void Set(Opacity_tag, int op) {}
    
    //draw rectangle
    void Draw(Rgectangle_tag, int x, int y, int w, int h) {}
    
    //draw image
    void Draw(Texture_tag, Texture tex, int w, int h) {}
    

    为了使其正常工作,您还需要:

    struct Color_tag{};
    struct Opacity_tag{};
    struct Rectangle_tag{};
    struct Texture_tag{};
    
    Color_tag color;
    Opacity_tag opacity;
    Rectangle_tag rectangle;
    Texture_tag texture;
    

    那么您的代码将如下所示:

    Set(color, 255, 255, 255);
    Set(opacity, 128);
    Draw(rectangle, 0, 0, 50, 50);
    Set(opacity, 255);
    Draw(texture, Textures::Woman, 75, 75);
    

    这种工作方式非常强大,可以有这样的东西:

    //set color
    void Set(Color_tag, int r, int g, int b) {}
    
    //set Hex color (HTML style)
    void Set(Color_tag, int color) {}
    
    //set opacity, same parameters like set hex color
    void Set(Opacity_tag, int op) {}
    

    不过,我更喜欢类似于 Blitz Rakete 建议的正常重载。

    【讨论】:

    • OP 也可以将标签类型作为模板参数传递...
    【解决方案4】:

    使用单个参数来编码其类型中的特定类型的操作,而不是模板参数。

    class Color {
        private:
            uint8_t r_;
            uint8_t g_;
            uint8_t b_;
        public:
            Color(uint8_t r, uint8_t g, uint8_t b):
                r_{r}, g_{g}, b_{b} {};
    };
    
    class Opacity {
        private:
            uint8_t alpha_;
        public:
            Opacity(uint_8 alpha): alpha_{alpha} {};
    };
    
    class Rectangle {
        private:
            uint32_t x_;
            uint32_t y_;
            uint32_t w_;
            uint32_t h_;
        public:
            Rectangle(uint32_t x, uint32_t y, uint32_t w, uint32_t h):
                x_{x}, y_{y}, w_{w}, h_{h} {};
    };
    
    // etc.
    

    然后使用函数重载而不是模板。

    class Context {
        public:
            Set(const Color& c);
            Set(const Opacity& o);
            Draw(const Rectangle& rect);
            // etc.
    }
    

    使用情况至少非常接近您尝试实现的目标。

    Set(Color{255, 255, 255});
    Set(Opacity{128});
    Draw(Rectangle{0, 0, 50, 50});
    Set(Opacity{255});
    Draw(Texture{Textures::Woman, 75, 75});
    

    您还可能希望有不同的 Rectlangle 构造函数,一个有两个点,一个有一个点和宽度和高度。在那里你可以使用相同的策略:

    class Width {
        private:
            uint32_t w_
        public:
            Width(uint32_t w): w_{w} {};
            operator uint32_t () { return w_; };
    };
    class Height {
        // same as Width
    };
    
    class Rectangle {
        private:
            uint32_t x1_;
            uint32_t y1_;
            uint32_t x2_;
            uint32_t y2_;
        public:
            Rectangle(uint32_t x, uint32_t y, Width w, Height h):
                x1_{x}, y1_{y}, x2_{x+w}, y2_{y+h} {};
            Rectangle(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2):
                x1_{x1}, y1_{y1}, x2_{x2}, y2_{y2} {};
    };
    
    // usage
    Draw(Rectangle{0, 0, Width{50}, Height{50}}); // use width and height
    Draw(Rectangle{0, 0, 50, 50}); // use two points
    

    你甚至可以更进一步,定义一个 Point 类型,这样绘制一个矩形就会像这样:

    Draw(Rectangle{Point{0, 0}, Width{50}, Height{50}}); // use width and height
    Draw(Rectangle{Point{0, 0}, Point{50, 50}}); // use two points
    

    使用类似的技术在表达意图和减少噪音之间找到完美的平衡。

    当然,您也可以在 Context 中添加简单的模板函数:

    template<typename T, typename A...>
    Set(A&& args...) {
        Set(T{std::forward(args)...});
    }
    
    template<typename T, typename A...>
    Draw(A&& args...) {
        Draw(T{std::forward(args)...});
    }
    

    现在您可以按照最初的预期使用整台机器了。

    Set<Color>(255, 255, 255);
    Set<Opacity>(128);
    Draw<Rectangle>(0, 0, 50, 50);
    Set<Opacity>(255);
    Draw<Texture>(Textures::Woman, 75, 75);
    
    // With width and height types
    Draw<Rectangle>(0, 0, Width{50}, Height{50});
    Draw<Rectangle>(0, 0, 50, 50);
    // With Point Type
    Draw<Rectangle>(Point{0, 0}, Width{50}, Height{50});
    Draw<Rectangle>(Point{0, 0}, Point{50, 50});
    

    【讨论】:

      【解决方案5】:

      创建方法 SetColor(int,int,int)、SetOpacity(int) 等有什么不干净的地方?如果您希望标题简单,您可以编写宏来简化 get/set 创建。

      例如

      #define SYNTHESIZE(TYPE, VARNAME, FUNCNAME) \
      protected: TYPE VARNAME;\
      public: virtual TYPE get##FUNCNAME(void) const { return VARNAME; }\
      public: virtual void set##FUNCNAME(TYPE var){ VARNAME = var; }
      

      使用

      class Foo
      {
         SYNTHESIZE(float, bar, Bar);
      }
      

      正如@tobspr 所说,您不能在不使用模板或修改编译器的情况下使用 语法。但是,有一些方法可以专门化模板,以便您可以在 cpp 中定义它。

      见:Explicit instantiation - when is it used?

      【讨论】:

        猜你喜欢
        • 2021-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-07
        • 1970-01-01
        相关资源
        最近更新 更多