Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/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+中的CRTP调度+;11_C++_Templates_C++11_Crtp - Fatal编程技术网

C++ C+中的CRTP调度+;11

C++ C+中的CRTP调度+;11,c++,templates,c++11,crtp,C++,Templates,C++11,Crtp,假设我有以下代码: template <class Derived> class Base { public: virtual void foo_impl() = 0; void foo() { static_cast<Derived*>(this)->foo_impl(); //A (*static_cast<Derived*>(this)).foo_impl(); //B } }; class Derive

假设我有以下代码:

template <class Derived>
class Base {
public:
   virtual void foo_impl() = 0;
   void foo() {
      static_cast<Derived*>(this)->foo_impl(); //A
      (*static_cast<Derived*>(this)).foo_impl(); //B
   }
};

class Derived : public Base<Derived> {
private:
   void foo_impl() {
      bar();
   }
};
模板
阶级基础{
公众:
虚拟void foo_impl()=0;
void foo(){
静态_cast(this)->foo_impl();//A
(*static_cast(this)).foo_impl();//B
}
};
派生类:公共基{
私人:
void foo_impl(){
bar();
}
};
有几个问题:

行A是否会生成虚拟函数调用?尽管我在互联网上找到的大多数东西都建议这样做,但考虑到指向派生的指针实际上仍然可以指向类型为Derived2(其中Derived2:public-Derived)的对象,我不知道编译器如何进行静态调度

B行是否解决了我在上一点中提出的问题(如果适用)?考虑到现在调用不再是指针上的调用,因此使用*,它看起来是这样的。将避免虚拟函数调用。但是,如果编译器将取消引用的强制转换视为引用类型,它仍然可以生成虚拟函数调用。。。在这种情况下,解决方法是什么

将C++11 final关键字添加到foo_impl()是否会改变编译器在任何(或任何其他相关)情况下的行为

行A是否会生成虚拟函数调用

foo_impl()
是虚拟的,
派生的
覆盖它。尽管
Derived
中的
foo\u impl()
没有显式标记为
virtual
,但它在基类中,这足以使它成为一个虚拟函数

B行是否解决了我在上一点中提出的问题(如果适用)

。调用是在指针上还是在引用上并不重要:编译器仍然不知道您是在从
Derived
派生的类的实例上调用函数
foo\u impl()
,还是在
Derived
的直接实例上调用函数。因此,通过vtable执行调用

要明白我的意思:

#include <iostream>

using namespace std;

template <class Derived>
class Base {
public:
   virtual void foo_impl() = 0;
   void foo() {
      static_cast<Derived*>(this)->foo_impl();
      (*static_cast<Derived*>(this)).foo_impl();
   }
};

class Derived : public Base<Derived> {
public:
   void foo_impl() {
      cout << "Derived::foo_impl()" << endl;
   }
};

class MoreDerived : public Derived {
public:
   void foo_impl() {
      cout << "MoreDerived::foo_impl()" << endl;
   }
};

int main()
{
    MoreDerived d;
    d.foo(); // Will output "MoreDerived::foo_impl()" twice
}
#包括
使用名称空间std;
模板
阶级基础{
公众:
虚拟void foo_impl()=0;
void foo(){
静态_cast(this)->foo_impl();
(*static_cast(this)).foo_impl();
}
};
派生类:公共基{
公众:
void foo_impl(){

难道A行和B行中多余的措辞对你的行为毫无影响吗 生成的代码。我不知道是谁推荐这个(我从未见过) 但在实践中,它可能产生影响的唯一时间是 如果函数不是虚拟的,只需编写
foo\u impl()
,然后 完了

如果 编译器知道派生类型。我见过它用于 类向量类(其中有不同的实现, e、 g.向量的法线、稀疏度等):

模板
阶级基础
{
私人:
虚拟T&getValue(int索引)=0;
公众:
T&运算符[](int-index){return getValue(index);}
};
模板
派生类:公共基
{
私人:
虚拟T&getValue(整数索引)
{
返回运算符[](索引);
}
公众:
T&运算符[](索引)
{
//找到元素并返回它。
}
};
这里的想法是,您通常只通过引用进行工作 对于基类,但是如果性能成为问题,因为 如果您在一个紧密的循环中使用
[]
,您可以
动态地向
在循环之前使用派生类,并在派生类上使用
[]

类。

CRTP的常用习惯用法不涉及在基中声明纯虚函数。正如您在其中一条注释中提到的,这意味着编译器将不会强制执行派生类型中的成员定义(除了通过使用之外,如果在基中使用了
foo
,则需要在派生类型中存在
foo_impl

虽然我会坚持使用常见的习惯用法,不在基础中定义纯虚拟函数,但是,如果您确实觉得需要这样做,可以通过添加额外的限定来禁用动态分派:

template <class Derived>
class Base {
public:
   virtual void foo_impl() = 0;
   void foo() {
      static_cast<Derived*>(this)->Derived::foo_impl();
      //                           ^^^^^^^^^
   }
};
模板
阶级基础{
公众:
虚拟void foo_impl()=0;
void foo(){
静态_cast(this)->派生::foo_impl();
//                           ^^^^^^^^^
}
};

使用额外的限定条件
Derived::
将禁用动态分派,该调用将静态解析为
Derived::foo_impl
。请注意,这将带来所有常见的警告:您有一个带有虚拟函数的类,并为每个对象支付虚拟指针的费用,但您不能重写该虚拟函数函数是最派生的类型,因为CRTP基中使用的是阻塞动态分派…

语句a和B是100%等效的,根据内置的
->
运算符的定义。语句a和B是100%等效的
foo\u impl()
。额外的赘述就是这样。调用不是在指针上,而是在左值引用上-正如所说的,语义相同。我认为当函数声明为
final
时,编译器可能有足够的信息来进行静态调用,而不是虚拟调用,作为一种优化。不知道这种可能性有多大。好吧,那么,有没有办法在没有C++概念的情况下强制执行子类的接口呢?也许是表达式sFIEAE还是StasyAsAsDt?我已经看到了一些错误消息的样子……@罗伯逊:如果程序使用<代码> BAS::FoO ,那么编译器会高兴地尖叫:<代码>派生::FooYiIMPL/<代码>没有声明。e您需要什么?您可以禁用动态分派(添加了一个关于如何执行的答案),但真正的问题是您是否真的需要额外的vptr和复杂性来完成任何事情,或者您是否在不必要地使事情复杂化。@RobertMason:您可以通过调用
foo_impl()的函数来自动实现这一点
Base
类中。如果您被错误消息的长度所困扰
template <class Derived>
class Base {
public:
   virtual void foo_impl() = 0;
   void foo() {
      static_cast<Derived*>(this)->Derived::foo_impl();
      //                           ^^^^^^^^^
   }
};