Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 接口范例性能(动态绑定与泛型编程)_C++_Metaprogramming_Dynamic Binding - Fatal编程技术网

C++ 接口范例性能(动态绑定与泛型编程)

C++ 接口范例性能(动态绑定与泛型编程),c++,metaprogramming,dynamic-binding,C++,Metaprogramming,Dynamic Binding,虽然动态绑定和模板的核心本质上是不同的,但它们可以用于实现相同的功能 代码示例(仅供参考) A) 动态绑定 名称空间数据库{ //接口 类自定义代码{ 公众: 虚空运算符()(字符)常量=0; }; 类库{ 公众: 无效功能(自定义代码常量和c){ c(‘d’); } }; //用户代码 类别MyCode1:公共自定义代码{ 公众: void运算符()(字符i)常量{ 哪一个更好?这取决于。你关注的是重叠。更好的是关注两种方法的分歧。你也忽略了需要同时使用两种方法的地方 模板的最大优势在于,它们

虽然动态绑定和模板的核心本质上是不同的,但它们可以用于实现相同的功能

代码示例(仅供参考) A) 动态绑定
名称空间数据库{
//接口
类自定义代码{
公众:
虚空运算符()(字符)常量=0;
};
类库{
公众:
无效功能(自定义代码常量和c){
c(‘d’);
}
};
//用户代码
类别MyCode1:公共自定义代码{
公众:
void运算符()(字符i)常量{

哪一个更好?这取决于。你关注的是重叠。更好的是关注两种方法的分歧。你也忽略了需要同时使用两种方法的地方

模板的最大优势在于,它们能够减少(有时会大大减少)cookie cuter代码。模板的另一个优势是元编程。得益于SFINAE,您可以做一些真正奇怪的事情

模板的一个缺点是语法有点笨拙。没有办法解决这个问题。事实就是这样。模板的另一个缺点是,每个实例化都是一个不同的类,与从同一模板类实例化的其他类完全无关。有一种方法可以解决这个问题:将两种方法结合起来。让你的mplate类派生自一些非模板基类。当然,现在您已经失去了一些运行时优势

多态性的最大优点是它是动态的。这可能是一个巨大的胜利。不要小看它。这种多态性会带来性能损失,但如果您想要拥有一个遵循公共接口但不同对象有不同实现的对象集合,您将以某种方式支付这种损失对于该接口

这些范式是否有一般结果/比较研究

从我所看到的,许多证明的例子可以在文章和出版物中找到。你最喜欢的C++书籍应该提供几个演示;如果你没有这样的资源,你可能想读现代C++设计:泛型编程和应用的设计模式——A. Alexandrescu。请记住,直接回答您的问题。同样,结果也会因实现和编译器的不同而不同,甚至编译器设置也会极大地影响此类测试的结果。(回答您的每个问题,尽管这不符合此特定问题的答案)

提速是否显著

简短回答:这要看情况而定

在您的示例中,编译器实际上可以使用静态分派,甚至可以内联虚拟函数调用(编译器可以看到足够的信息)。现在,我将把响应从一个简单的示例(特别是OP)转移到更大、更复杂的程序

扩展到“视情况而定”:是的,速度可以是无法测量到的,也可以是巨大的。你必须这样做(而且很可能已经这样做了)认识到编译器可以通过泛型在编译时提供大量信息。然后它可以使用这些信息更准确地优化程序。一个很好的例子是使用
std::array
vs
std::vector
。vector在运行时增加了灵活性,但成本可能相当高很重要。向量需要实现更多的调整大小,动态分配的需要可能代价高昂。还有其他区别:数组的备份分配不会改变(++优化),元素计数是固定的(++优化),而且在许多情况下,无需调用新的

你现在可能认为这个例子与原来的问题有很大的不同。在许多方面,它实际上并没有什么不同:随着程序复杂性的增加,编译器对程序的了解越来越多。这些信息可以删除程序的几个部分(死代码)以
std::array
为例,该类型提供的信息足以让编译器轻松地说“哦,我看到这个数组的大小是七个元素,我将相应地展开循环”您将拥有更少的指令,并消除了预测失误。还有很多,但在数组/向量的情况下,我看到优化程序的可执行大小在从
vector
转换到类似于
array
的界面时减少到20%。此外,代码的执行速度可以快几倍。事实上ome表达式完全可以在编译时计算

动态调度仍然有它的优点,如果正确使用,使用动态调度也可以提高程序的速度-您真正需要学习的是决定何时偏爱一个而不是另一个。类似于具有多个变量的大型函数无法非常有效地优化(真实程序中所有模板扩展的结果),在许多情况下,虚拟函数调用实际上是一种更快、更干净的方法。因此,它们是两个独立的特性,您需要一些实践来确定什么是正确的(许多程序员没有花足够的时间来充分了解这一点)

总之,它们应该被视为单独的特性,适用于不同的场景。这些特性(imho)的实际重叠比它们在现实世界中的实际重叠要小得多

编译时间呢

使用模板,开发过程中的编译和链接时间可能相当长。每次头/模板更改时,您都需要对所有依赖项进行编译——这通常是支持动态调度的一大好处。如果您提前计划并适当构建,当然可以减少这一点——了解m是如何实现的有了模板,你不仅可以提高学习的频率
namespace DB {
  // interface
  class CustomCode {
    public:
      virtual void operator()(char) const = 0;
  };
  class Lib {
    public:
      void feature(CustomCode const& c) {
        c('d');
      }
  };

  // user code
  class MyCode1 : public CustomCode {
    public:
      void operator()(char i) const {
        std::cout << "1: " << i << std::endl;
      }
  };
  class MyCode2 : public CustomCode {
    public:
      void operator()(char i) const {
        std::cout << "2: " << i << std::endl;
      }
  };

  void use() {
    Lib lib;
    lib.feature(MyCode1());
    lib.feature(MyCode2());
  }
}
namespace GP {
  //interface
  template <typename CustomCode> class Lib {
    public:
      void feature(CustomCode const& c) {
        c('g');
      }
  };

  // user code
  class MyCode1 {
    public:
      void operator()(char i) const {
        std::cout << "1: " << i << std::endl;
      }
  };
  class MyCode2 {
    public:
      void operator()(char i) const {
        std::cout << "2: " << i << std::endl;
      }
  };

  void use() {
    Lib<MyCode1> lib;
    lib.feature(MyCode1());
    //lib.feature(MyCode2());  <-- illegal
  }
}