【问题标题】:Use templates to clone class types exactly?使用模板准确地克隆类类型?
【发布时间】:2013-09-23 03:46:24
【问题描述】:

我有一系列 C++ 类,我希望它们在功能上都相同,但在其他方面与继承无关。实际上,这些类只会在名称上有所不同。 (这些类将被抛出,我不希望某个基类的 catch 子句吞噬抛出的派生对象。将有派生类,但我希望创建离散的抛出类集就捕获块而言,始终是隔离的。)

当然,这样做的缺点是复制源代码。每当需要更改某些内容时,我不想更新同一代码的 N 个副本。

我已经通过#define 解决了代码重复问题。但我认为,如果我可以利用模板,它将有助于调试能力。模板中唯一参数化的就是类名本身。

我尝试了以下方法,但在 gcc 中不起作用(启用了 c++0x 支持):

template<typename ClassName>
class ClassName
{
  public:
    ClassName(int foo, float bar) { ... }
   ~ClassName() { ... }

   bool SomePublicMethod() { ... }

  private:
    ...
}

然后我会用类似的东西声明实际的类:

typedef ClassName<UnrelatedClass1> UnrelatedClass1;
typedef ClassName<UnrelatedClass2> UnrelatedClass2;

我已经知道上面的行不通了;我将它作为我想要完成的概念示例提供,并且想知道除了我目前使用的#define 宏方法之外是否有其他方法可以使它工作(它的调试能力降低了。 )

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    使用价值专用模板:

    template<int ID>
    class ClassName
    {
      public:
        ClassName(int foo, float bar) { ... }
       ~ClassName() { ... }
    
       bool SomePublicMethod() { ... }
    
      private:
        ...
    }
    
    typedef ClassName<1> UnrelatedClass1;
    typedef ClassName<2> UnrelatedClass2;
    

    【讨论】:

    • 或者更好的是,使用空类作为标签:这样您就不必担心标签分配或冲突,并且可以使用有意义的名称。 template &lt;typename Tag&gt; class ClassName {...}; struct SomeTag{}; typedef ClassName&lt;SomeTag&gt; UnrelatedClass1;
    • @IgorTandetnik,更好,typedef ClassName&lt;struct UnrelatedClass1Tag&gt; UnrelatedClass1; :)
    • @chris:是的,在我发送评论后一秒钟我就知道你在这里做什么(自删除以来)。
    • @chris:我邀请你回答这个问题,以便我可以接受(在我尝试之后)。
    • @chris:你的意思是this post,也许吧?
    【解决方案2】:

    这听起来根本不是一个好主意。

    异常类应捕获特定类型的错误,并使用继承来更概括错误类型。

    因此,例如,您可能有一个“磁盘崩溃异常”,更一般地说可能是“磁盘异常”,更一般地说是“io 异常”,其核心始终是“std::exception”

    如果您的所有异常都捕获不同类型/类别的错误,那么为什么它们都具有相同类型的实现。

    此外,在模板中混合使用 #define 并不常见,因为当出现编译器错误时,这总是会降低其可读性(即使在没有导致错误时它看起来更易读)。

    也许你可以提供更多关于你的异常类中实现了什么的信息,我可以看看我是否可以进一步帮助你。

    【讨论】:

    • 1.它们都相同的原因是我重新发明了自己的专业“std::exception”。我的是一个抽象基类,用于我将使用此模板克隆的所有异常类。 2. 我想创建不混合的异常类别。特别是:RVBAssertion(只会被 main 捕获)和 RVBException(其他一切都将从中派生,即 IOException、DeviceException 等,正如您所描述的那样。)因为...
    • ...我的抽象类永远无法被捕获(因为它无法被实例化)并且 RVBAssertion 和 RVBException 是类型兄弟,我永远不必担心 RVBException 的捕获块吞噬一个 RVBAssertion,反之亦然。
    • 一个抽象类只需要一个抽象成员是抽象的(并且是不可满足的),它仍然可以在其他成员中提供功能。因此,您对 std::exception 的替换可以具有 RVBAEx 和 RVAAs 的所有通用代码,但仍然是抽象的。只有接口不能有任何实现,所有原型都必须是纯/抽象的。
    【解决方案3】:

    当他们说你应该使用继承时,我同意其他人的观点。它有很多用途(其中一个是您希望拥有类似课程的原因)。仅当对象相关时才编写派生类不是强制性的 - 即使只是匹配的功能也很好,因为您的想法确实是将相似的代码放在一起。

    但是,由于您的查询是关于创建多个类,而我们对您的项目没有足够的了解,我相信您可能真的需要单独的类。一种方法是通过宏。这是一个示例:

    #include <iostream>
    using  std::cout;
    using  std::endl;
    
    #define CUSTOM_CLASS(_CL) class _CL\
    {\
          public:\
                      _CL(int foo, float bar) { cout << "Creating instance with foo=" << foo << ";bar=" << bar << endl;}\
           ~_CL() { }\
           bool SomePublicMethod() { cout << "Class created\n"; }\
    };
    
    CUSTOM_CLASS(myclass1);
    CUSTOM_CLASS(myclass2);
    
    int main()
    {
        myclass1 instance1(1, 1.3f);
        myclass2 instance2(2, 0.3f);
        return 0;
    }
    

    如果你使用 g++ 运行它,你会得到以下结果:

    Creating instance with foo=1;bar=1.3
    Creating instance with foo=2;bar=0.3
    

    【讨论】:

    • 请再次阅读我的问题。我确实有继承权;我只有一个角落案例,我不想继承,但想要相同的代码。您上面提出的解决方案使用#define,与我自己的解决方案基本相同。我在 SO 上的问题是,是否可以使用 C++ 模板完成同样的事情。我在上面接受的答案提供了多种选择,所有这些都对我有用(但在我看来,@chris 的评论是最好的。)
    • _CL 是保留标识符。此外,这相当丑陋。模板是正确的解决方案,请参阅 Iuri Covalisin 的回答。
    猜你喜欢
    • 1970-01-01
    • 2018-03-26
    • 2021-10-26
    • 2014-01-09
    • 1970-01-01
    • 2011-01-19
    • 2015-06-16
    • 1970-01-01
    • 2013-12-18
    相关资源
    最近更新 更多