C++ C++;:奇怪的重复模板模式是什么?奇怪的是,重复出现的模板模式能取代虚拟函数吗?

C++ C++;:奇怪的重复模板模式是什么?奇怪的是,重复出现的模板模式能取代虚拟函数吗?,c++,templates,virtual-functions,crtp,C++,Templates,Virtual Functions,Crtp,我对这个问题没有一个精确的描述,所以我只是问这是否可能(如果可能的话,其他一些信息会很好) 一位程序员告诉我,您可以避免由虚拟函数/多态性引起的运行时开销。他说,为了避免运行时开销,您可以在名为“奇怪的重复性模板模式”的模式中使用模板,该模式如下所示: class Derived : public Base<Derived> { // ... implementation here }; class ProvidesMethod { protected: void Meth

我对这个问题没有一个精确的描述,所以我只是问这是否可能(如果可能的话,其他一些信息会很好)

一位程序员告诉我,您可以避免由虚拟函数/多态性引起的运行时开销。他说,为了避免运行时开销,您可以在名为“奇怪的重复性模板模式”的模式中使用模板,该模式如下所示:

class Derived : public Base<Derived>
{
  // ... implementation here
};
class ProvidesMethod {
protected:
  void Method() {
    // do something
    Part1();
    // do something else
    Part2();
    // do something final
  }

private:
  virtual void Part1() = 0;
  virtual void Part2() = 0;
};

class ExposesMethod : private ProvidesMethod {
public:
  using ProvidesMethod::Method;

private:
  void Part1() {
    // first part implementation
  }
  void Part2() {
    // second part implementation
  }
};
template <typename Derived>
class ProvidesMethod {
protected:
  void Method() {
    // do something
    self().Part1();
    // do something else
    self().Part2();
    // do something final
  }

private:
  Derived& self() { return *static_cast<Derived*>(this); }
  const Derived& self() const { return *static_cast<const Derived*>(this); }
};

class ExposesMethod : private ProvidesMethod<ExposesMethod> {
public:
  using ProvidesMethod<ExposesMethod>::Method;

private:
  friend class ProvidesMethod<ExposesMethod>;
  void Part1() {
    // first part implementation
  }
  void Part2() {
    // second part implementation
  }
};
派生类:公共基
{
//…在这里执行
};
这种奇怪的重复模板模式是如何工作的

如何使用奇怪的循环模板模式来替代正常的虚拟函数/多态性


我弄错了吗?

我不确定您将如何使用模板来提供虚拟函数之类的东西-这对我来说似乎很奇怪-当然没有一大堆技巧,这些技巧最终相当于实现您自己版本的虚拟函数,而不是使用编译器提供的虚拟函数,我发现很难理解如何使调用函数的代码不知道对象是什么类型,并为该对象类型调用正确的函数。这就是虚拟函数所做的

此外,根据个人经验,虚拟函数的实际开销非常小,只有在非常极端的情况下才会产生影响(非虚拟函数内联的主要情况下,调用函数的开销非常小,因此调用函数的开销占总执行时间的很大一部分)(这也意味着函数需要多次调用才能产生显著的差异)。如果您必须以其他方式(除了在“虚拟函数”上实现您自己的变体之外)来“确定要做什么”(使用if语句或类似的方式),则开销会更大,但这只是重新发明轮子,除非您的新轮子比现有轮子好,否则这不是一个好主意]。

这被称为CRTP(用于奇怪的重复模板模式),因此您可以查找它

虽然我真的不知道它如何取代经典多态性


另一方面,在某些情况下,可以通过模板替换类的复杂层次结构(有关更多信息,请参阅基于策略的设计),但这并不总是可能的…

正如Julien所指出的,这是CRTP。您应该查找它。 但是CRTP不能代替虚拟函数。如果它在特定情况下为您所用,那么您实际上并不需要虚拟函数。
看,模板提供编译时多态性。虚拟函数提供运行时多态性。如果您不知道在编译时将调用哪些重写,那么基于模板的解决方案将不起作用。如果您总是知道在运行时对象的真实类型,那么您就不需要虚拟函数

非常具体地说,可以使用CRTP代替具有虚拟函数的基类来实现,而无需虚拟函数调用开销

使用虚拟函数时,TMP如下所示:

class Derived : public Base<Derived>
{
  // ... implementation here
};
class ProvidesMethod {
protected:
  void Method() {
    // do something
    Part1();
    // do something else
    Part2();
    // do something final
  }

private:
  virtual void Part1() = 0;
  virtual void Part2() = 0;
};

class ExposesMethod : private ProvidesMethod {
public:
  using ProvidesMethod::Method;

private:
  void Part1() {
    // first part implementation
  }
  void Part2() {
    // second part implementation
  }
};
template <typename Derived>
class ProvidesMethod {
protected:
  void Method() {
    // do something
    self().Part1();
    // do something else
    self().Part2();
    // do something final
  }

private:
  Derived& self() { return *static_cast<Derived*>(this); }
  const Derived& self() const { return *static_cast<const Derived*>(this); }
};

class ExposesMethod : private ProvidesMethod<ExposesMethod> {
public:
  using ProvidesMethod<ExposesMethod>::Method;

private:
  friend class ProvidesMethod<ExposesMethod>;
  void Part1() {
    // first part implementation
  }
  void Part2() {
    // second part implementation
  }
};
对于CRTP,它看起来是这样的:

class Derived : public Base<Derived>
{
  // ... implementation here
};
class ProvidesMethod {
protected:
  void Method() {
    // do something
    Part1();
    // do something else
    Part2();
    // do something final
  }

private:
  virtual void Part1() = 0;
  virtual void Part2() = 0;
};

class ExposesMethod : private ProvidesMethod {
public:
  using ProvidesMethod::Method;

private:
  void Part1() {
    // first part implementation
  }
  void Part2() {
    // second part implementation
  }
};
template <typename Derived>
class ProvidesMethod {
protected:
  void Method() {
    // do something
    self().Part1();
    // do something else
    self().Part2();
    // do something final
  }

private:
  Derived& self() { return *static_cast<Derived*>(this); }
  const Derived& self() const { return *static_cast<const Derived*>(this); }
};

class ExposesMethod : private ProvidesMethod<ExposesMethod> {
public:
  using ProvidesMethod<ExposesMethod>::Method;

private:
  friend class ProvidesMethod<ExposesMethod>;
  void Part1() {
    // first part implementation
  }
  void Part2() {
    // second part implementation
  }
};
模板
类ProvidesMethod{
受保护的:
void方法(){
//做点什么
self().Part1();
//做点别的
self().Part2();
//做最后的事
}
私人:
派生的&self(){return*static_cast(this);}
const-Derived&self()const{return*static_cast(this);}
};
类ExposesMethod:private ProvidesMethod{
公众:
使用ProvidesMethod::Method;
私人:
友元类提供方法;
无效第1部分(){
//第一部分实施
}
无效部分2(){
//第二部分实施
}
};

一个人不能。两者完全不同,解决的问题也不同。然而,通常情况下,人们在应该使用Y时错误地使用X,然后建议“使用Y而不是X”。这并不意味着X和Y是相关的,只是人们不知道他们在做什么。哇,这是一种模式。我同意CRTP可能会增加模糊性,因为现实生活中的性能在很大程度上会提高。然而,内联代码的能力在必须快速运行的短函数中是一件非常重要的事情。它避免了函数调用开销,避免了堆栈使用,并可以提高缓存。虚拟函数所需的间接查找可能非常重要。我同意,但如果您确实需要根据它是哪个类执行不同的操作,则需要有一个
if
开关
、或
函数指针
类型解决方案。虚拟几乎总是最有效的解决方案这些。只有当vtable不在缓存中且
this
指针不在缓存中时,查找才有意义。如果您正在使用某些成员数据,至少需要将
this
指针加载到缓存中。@matstpeterson不必有任何选择语句。CRTP“模拟”类似这样的虚拟分派:
模板类基类{void foo(){((派生*)this)->pseudoVirtualBar();}
这将根据模板参数(即派生类)调用不同的函数。对,因此在执行“手动”时,它将类型转换隐藏为正确的类型多态性。如果您在编译时知道要选择什么,这很好。但是,您也可以使用更明显的强制转换来解决这种情况。无论哪种方式,除非我们遇到极端情况(例如,返回一个整数,或两个整数成员的总和,或类似的琐事),虚拟函数具有相当低的开销。我相信CRTP旨在“看起来”像多态结构,但所有绑定都是在编译时进行的;因此没有运行时开销。正如@Mats所述,如果您必须执行大量的ifs、开关等来模拟运行时多态性,那么最好使用真实的virt调用。为什么“self”是方法?为什么不直接调用Part();?因为那样会使用