【问题标题】:Template specialization of pure virtual function纯虚函数的模板特化
【发布时间】:2019-11-19 02:43:50
【问题描述】:

如何专门化在基类中定义为纯函数的模板化函数?

struct A {
    virtual void func(int a) = 0;
    //virtual void func(int a) {} // replace above line with this and it works
};

struct B : public A {
    template<typename T> void func(T t) {
        cout <<"hello"<<endl;
    }
};
template<> void B::func<int>(int a) { cout <<"hello 2"<<endl; }


int main() {
    B b;
    b.func(2);
}

错误:

错误:变量类型“B”是一个抽象类 乙乙; ^ 注意:“B”中未实现的纯虚方法“func” 虚拟 void func(int a) = 0;

【问题讨论】:

  • 模板和虚函数不能混用。
  • 我相信这不是重复的,因为覆盖是模板专用的。还要注意我只有纯虚函数有问题。
  • @Nujufas 问题有点不同,但答案是一样的。 Member function templates cannot be declared virtual.
  • @StackDanny 可以接受,你能否解释一下如果函数被设为“不纯”,它为什么会起作用?

标签: c++ templates virtual-functions


【解决方案1】:

虚拟函数只能被非模板函数覆盖。在这种情况下,

那么 Derived 类中的这个函数也是虚拟的(无论在其声明中是否使用关键字 virtual)并覆盖 Base::vf(无论在其声明中是否使用单词 override)。

注意函数模板不能是virtual functions

函数模板不能声明为虚拟的。

来自标准,[temp.mem]/4

成员函数模板的特化不会覆盖 来自基类的虚函数。 [ 示例:

class B {
  virtual void f(int);
};

class D : public B {
  template <class T> void f(T); // does not override B​::​f(int)
  void f(int i) { f<>(i); }     // overriding function that calls the template instantiation
};

— 结束示例 ]

关于你的问题,

如果函数被设为“非纯”,为什么它会起作用?

编译错误消失了,但它仍然无法按您的预期工作;派生类中的函数模板不会覆盖基类的虚函数。可以通过dynamic dispatch查看:

如果使用基类的指针或引用处理派生类,则调用被覆盖的虚函数将调用派生类中定义的行为。

请注意,您应该使用指针或引用来进行动态调度,例如

B b;
A* pa = &b;
pa->func(2);

LIVE

您也可以申请override specifier 来帮助您确认覆盖

【讨论】:

  • 我没有将函数模板声明为虚拟的。
  • 你只能用虚函数覆盖。覆盖是隐式虚拟的。尝试将您的功能标记为override
  • @GuillaumeRacicot 如果覆盖的函数是模板化的,它仍然无法工作。
  • @StackDanny Pertty 确定他们是在说标记它override,这样他们就会收到一个编译器错误,提示无法使用模板。
  • 标准中引用的示例准确地展示了如何使派生类像模板一样覆盖虚函数,如果这是你想要的:定义一个只调用模板的精确覆盖。在问题的示例中,这类似于 B 定义中的 void func(int a) override { func&lt;&gt;(a); }
【解决方案2】:

//virtual void func(int a) {} // 用这个替换上面的行就可以了

替换上面那行代码编译,不行。

或者,更好的是,工作但不是你所期望的。

问题在于virtual 函数和template 函数不能很好地混合。

所以你不能制作一个直接覆盖虚函数的模板函数:如果你将func()定义为一个空虚函数

virtual void func(int a) = 0;

基类 A 和所有派生类将变为不可实例化,除非您未定义有效的 virtual func() 函数。

定义

virtual void func(int a) {}

A 基类和所有派生类不再不可实例化,因为您不再需要重新定义虚函数。

templatefunc()版本与虚函数无关。

当您在main() 中调用b.func(2) 时,调用的是template,而不是继承自Avirtual func()。这是因为templatefunc()“隐藏”了func()继承了virtual版本。

您可以在B 中“取消隐藏”virtual func() 版本,添加到B 定义的正文中

using A::func;

这样,在main()中调用b.func(2);,被A继承的virtual版本被调用,func()的模板特化,所以std::cout &lt;&lt;"hello 2" &lt;&lt; std::endl;指令不再执行。

现在...如果我理解正确,您需要一个 template func() 函数,以防 T == int 加入虚拟专业化。

我看到的唯一方法是在B 中定义virtual override

void func (int a) override // override, so necessarily virtual
 { std::cout <<"hello 2" << std::endl; }  

并从template 专业化调用它

template <>
void B::func<int> (int a)
 { func(a); } // call the virtual override version

以下是完整的编译示例

#include <iostream>

struct A
 { virtual void func(int a) = 0; };

struct B : public A
 {
   void func (int a) override
    { std::cout <<"hello 2" << std::endl; }

   template<typename T>
   void func (T t)
    { std::cout << "hello" << std::endl; }
 };

template <>
void B::func<int> (int a)
 { func(a); }


int main ()
 {
   B{}.func(2); // call directly virtual func()
   B{}.func<int>(2); // call template func() specialization that call virtual func()
 }

【讨论】:

  • 非常有趣的答案。
【解决方案3】:

这可能会被考虑在内,但是在使用模板进行设计时可以使用一个非常好的助记符:

虚函数——动态多态(运行时通过vtable解决)

模板特化 - 静态多态(在编译时通过类型信息解决)

不要试图解决另一个问题。

在您的情况下,您正试图通过模板专业化为虚拟方法提供主体(解决运行时多态性)。

【讨论】:

    猜你喜欢
    • 2016-10-14
    • 2012-11-04
    • 1970-01-01
    • 2010-11-19
    • 2014-03-08
    • 1970-01-01
    • 2017-06-28
    • 1970-01-01
    相关资源
    最近更新 更多