为啥 C++ 中的模板 mixin 不再是主流?

Posted

技术标签:

【中文标题】为啥 C++ 中的模板 mixin 不再是主流?【英文标题】:Why are template mixins in C++ not more of a mainstay?为什么 C++ 中的模板 mixin 不再是主流? 【发布时间】:2012-02-04 01:00:18 【问题描述】:

我在 C++ 中经常使用模板混合,但我想知道为什么没有更多地使用该技术。这似乎是重用的终极目标。这种功能和效率的结合是我真正喜欢 C++ 并且无法看到自己转向 JIT 语言的原因之一。

这篇文章:http://www.thinkbottomup.com.au/site/blog/C%20%20_Mixins_-_Reuse_through_inheritance_is_good 是一个很好的背景资料,如果您不知道它们是什么,并且在重用和性能方面如此清楚地说明了案例。

【问题讨论】:

JIT 语言可以与 C++ 一样强大,但对于大多数处理器密集型应用程序除外。 @GMan:我认为 JIT 参考与问题无关。这是一个很好的问题 - 这是一个有趣的设计模式,我从未在我从事的任何代码库中看到过。 @Skizz:这完全不相关,我同意;但它就在那里。 +1:从来没有意识到这种技术可以用来在C++中实现mixins! @JessePepper:我猜他们认为这是一个“边缘”问题,因为答案可能相当主观(“我见过很多”类型?)。至于 JIT:这不是 JIT 的问题,更多的是动态与静态类型的问题。动态类型使它更容易(在语法上),因为您编写的所有方法都是 C++ 意义上的“模板”。当然,您会为运行时错误付出代价... 【参考方案1】:

mixins 的问题是......构造。

class Base1  public: Base1(Dummy volatile&, int); ;

class Base2  public: Base2(Special const&, Special const&); ;

现在,我的超级混音器:

template <typename T>
struct Mixin: T ;

您注意到这里的问题了吗?我到底应该如何将参数传递给基类的构造函数? Mixin应该提出什么样的构造函数?

这是一个难题,直到 C++11 增强了语言以获得完美的转发。

// std::foward is in <utility>

template <typename T>
struct Mixin: T 
  template <typename... Args>
  explicit Mixin(Args&&... args): T(std::forward<Args>(args...)) 
;

注意:欢迎进行双重检查

所以现在我们真的可以使用 mixins...而且只需要改变人们的习惯 :)

当然,我们是否真的想要是完全不同的主题。

mixins 的一个问题(你引用的那篇糟糕的文章很高兴跳过)是你完全失去了依赖隔离......以及LoggingTask 的用户随后必然要编写模板方法这一事实。在非常大的代码库中,更多地关注依赖关系而不是性能,因为依赖关系消耗人力周期,而性能只消耗 CPU 周期......而且这些通常更便宜。

【讨论】:

这有点夸大了实际问题 - 可以转发少量有限数量的参数而有点乏味(模板 显式 Mixin(const X& x) : T(x) template 显式 Mixin(X&, Y&) : T(x,y) ...),或者是一个带有一些丑陋的预处理器调用的大有限数...... @TonyDelroy:哦,相信我,人们已经尝试过了。鉴于constvolatile&amp; 与“价值”的组合,它很快就会变得难以处理。当然,对于“一个”特定情况,它可能工作得很好,虽然它很困难。当然,正如大段所述,虽然它现在在技术上是可行的,但由于其他原因,它可能仍然不可取。 @Skizz 你刚刚回答了你自己的问题。 RAII 更受欢迎,被认为相当有用。 @Matthieu:为什么 LoggingTask 的用户一定要编写模板方法?文章中包含了一个 mixin 来添加多态基类,这样不行吗? @Matthieu:不过,他们已经提供了那个垫片。 如果你想避免虚拟调用,你编写模板函数。 如果您想避免使用模板函数,请使用TaskAdapter 并将其作为ITask&amp; 传递。他们并没有声称您可以同时做到这两点,但是其他人也没有,他们确实让您选择自己的喜好。我同意构造函数,我只是认为您的额外批评是不合理的。也就是说,可能应该有另一个多态适配器,它通过引用而不是继承来接受它包装的任务,以简化样式之间的边界。【参考方案2】:

模板要求实现在翻译单元中可见,而不仅仅是在链接时(如果您只使用指针或对实例化的引用,C++11 解决了这个问题)。这是企业环境中低级代码的一个主要问题:对实现的更改将触发(可能会或可能不会自动)大量库和客户端重新编译,而不仅仅是需要重新链接。

此外,每个模板实例化都会创建一个不同的类型,这意味着旨在处理任何模板实例化的函数必须能够接受它们——要么它们自己被强制模板化,要么它们需要某种形式的移交给运行时多态性(这通常很容易做到:只需要一个表达支持的操作集的抽象基类,以及一些“给我一个访问器”函数,该函数返回一个派生对象,该派生对象带有指向模板实例化的指针和虚拟中的相关整体调度表)。

无论如何,这些问题通常是可以管理的,但是管理所涉及的耦合、依赖项和接口的技术比简单的 mixin 技术本身要少得多,公开、理解和容易获得。顺便说一句,模板和策略类也是如此。

【讨论】:

我认为使用抽象接口是避免耦合和编译担忧的主要方式。我认为这是一个公平的评论。我们的团队相对较小,但我确实想知道代码库是否需要如此庞大和耦合。 如果我不得不冠冕堂皇的方式“主要”,我肯定会使用普通的老式离线实现(无虚拟调度),但 pImpl 和抽象接口也有它们的位置。无论如何,这些事情是可以管理的。另一种技术是为您想要的特定模板实例化一个非模板化前端,该模板直接支持外联实现,但通过标题不可见。许多减少或控制耦合的选项,以及便于在运行时和编译时多态性之间轻松移动的选项。

以上是关于为啥 C++ 中的模板 mixin 不再是主流?的主要内容,如果未能解决你的问题,请参考以下文章

通过模板的 C++ 混合:为啥这不起作用?

C++中,为啥函数参数不够也可以调用?而且函数模板定义中没有提供默认值。

为啥此代码有效(具有无效非模板函数的 C++ 模板类)? [复制]

为啥不能编译这个 C++ 模板代码?

c++创建链表为啥要用类模板

为啥接受数组的 C++ 模板并不比接受 GCC 5.3 和 Clang 4.0 的指针更专业?