【问题标题】:Enforce functions to implement for template argument class?强制执行函数来为模板参数类实现?
【发布时间】:2011-05-04 02:42:12
【问题描述】:

我应该定义一个接口,明确告知用户他/她应该实现什么,以便将类用作模板参数,还是让编译器在功能未实现时警告他?

template <Class C1, Class C2> 
SomeClass
{
  ...
}

C1 类必须实现某些方法和操作符,编译器在使用它们之前不会发出警告。我应该依靠编译器发出警告还是确保我这样做:

Class C1 : public SomeInterfaceEnforcedFunctions
{
   // Class C1 has to implement them either way 
   // but this is explicit? am I right or being 
   // redundant ?
}

【问题讨论】:

  • 你是多余的。如果你尝试调用 C1 没有的方法,它不会编译,这就足够了。
  • @PigBen:我不同意。如果你把它留给编译器,用户会经常得到一个可怕的、无用的错误信息。通过自己执行要求的一些工作,您可以改进很多。
  • @Jerry -- 这很公平。但是,在我看来,花时间学习理解错误消息会更好。它们不是无用的,只是难以破译。然后可以将该技能应用于理解其他错误,例如误用 STL 时遇到的错误。
  • @PigBen:我不同意——“正确”的答案是 STL 实现(使用你的例子)被写来检查它的参数。 IMO,没有很好的借口让用户接受模板中典型的错误消息。

标签: c++ templates code-generation


【解决方案1】:

理想情况下,您应该使用concept 来指定对用作模板参数的类型的要求。不幸的是,当前的即将发布的标准都不包括concepts。

除此之外,有多种方法可用于执行此类要求。您可能想阅读Eric Neibler's article,了解如何对模板参数强制执行要求。

我同意 Eric 的说法,即把它全部交给编译器通常是不可接受的。这是我们大多数人与模板相关联的可怕错误消息的大部分来源,其中看似微不足道的拼写错误可能会导致页面无法阅读。

【讨论】:

  • 还有一个疑问 - 你知道我怎样才能找出代码实际上是为哪些函数生成的吗? 如果我在模板中有很多函数但只使用其中一个(有没有比查找函数更好的方法)
【解决方案2】:

如果你要强制一个界面,那么为什么要使用模板呢?你可以简单地做 -

class SomeInterface //make this an interface by having pure virtual functions
{    
    public:
        RType SomeFunction(Param1 p1, Param2 p2) = 0; 
        /*You don't have to know how this method is implemented, 
          but now you can guarantee that whoever wants to create a type 
          that is SomeInterface will have to implement SomeFunction in 
          their derived class. 
        */
};

紧随其后

template <class C2>
class SomeClass
{
    //use SomeInterface here directly.       
};

更新 -

这种方法的一个基本问题是它只适用于用户推出的类型。如果存在符合您的接口规范的标准库类型,或者第三方代码或其他库(如 boost)具有符合 SomeInterface 的类,则除非您将它们包装在自己的类中,否则它们将无法工作,实现接口并适当地转发呼叫。我不知何故不再喜欢我的答案了。

【讨论】:

  • 因为我知道 我想从课堂上得到什么但还不知道怎么做,所以推迟hows 只定义what-all
  • 没关系。您将 SomeInterface 中的函数声明为纯虚方法,从而使其成为纯接口。
【解决方案3】:

缺少概念,一个现在被放弃的概念(双关语不是有意的,但已注明),用于描述模板参数必须满足哪些要求,要求只是隐式执行。也就是说,如果您的用户用作模板参数的任何内容都不能满足它们,则代码将无法编译。不幸的是,由此产生的错误消息通常很乱。你唯一能做的就是改善问题

  1. 在模板的文档中描述要求
  2. 在模板的早期插入检查这些要求的代码,以免深入研究以致用户收到的错误消息变得难以理解。 后者可能非常复杂(static_assert 救援!)甚至是不可能的,这就是概念被认为成为核心语言功能而不是库的原因。

请注意,以这种方式很容易忽略一个需求,只有当有人将一个类型用作不起作用的模板参数时,这种情况才会变得明显。然而,至少同样容易忽略要求往往很不完善,并且在描述中添加的内容比代码实际要求的内容更多。
例如,+ 不仅为数字定义,还为std::string 和任意数量的用户定义类型定义。因此,模板add&lt;T&gt; 不仅可以用于数字,还可以用于字符串和无限数量的用户定义类型。这是您想要抑制的代码的不良副作用还是您想要支持的功能取决于您。我要说的是,要抓住这一点并不容易。

我不认为以具有虚函数的抽象基类的形式定义接口是一个好主意。这就是运行时多态性,经典 OO 的主要支柱。如果这样做,则不需要模板,只需按引用获取基类即可。
但是你也失去了模板的主要优点之一,那就是它们在某些方面更灵活(尝试编写一个 add() 函数经典 OO,它适用于任何类型重载 + 中)并且速度更快,因为函数调用的绑定不是在运行时发生的,而是在编译期间发生的。 (由于内联的能力,这带来了比最初看起来更多的东西,这通常是运行时多态性所不可能实现的。)

【讨论】:

  • 感谢您的详细解释,confusion - 如果我不关心运行时开销,我会这样做吗?或者它仍然是一种糟糕的设计。 问这个是因为,认为某人在使用之前必须知道要实现什么并且没有干净的方法让他们知道,这很痛苦(我的知识有限,请提出更多的概念或观点我可能错过了)非常感谢。
  • @Gollum:嗯,问题是这样的:如果你把这些函数变成虚拟的,为什么还要用模板呢?经典的 OO 运行时多态性适用于此。您的模板用户可能会感到困惑的是,从您的基类派生的类型 not 也可以正常工作。 IMO,这基本上归结为设计决定。你想要运行时多态还是编译时多态?通常你想要后者以提高速度(编译时绑定和内联,virtual 杀死)和灵活性(鸭子打字:支持添加 operator+ 重载的所有内容)。
  • 如果你想要后者,你想要后者。诚然,由于 概念 推迟到标准的未来版本,编译时多态性缺乏明确说明用于模板参数的类型需要支持的能力。但这就是 C++(当前)的方式。如果你想要 C++ 中的编译时多态性,你需要忍受它。
猜你喜欢
  • 1970-01-01
  • 2015-08-01
  • 2021-10-30
  • 2019-11-23
  • 1970-01-01
  • 2020-05-12
  • 2016-08-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多