【问题标题】:Is making a function template specialization virtual legal?使功能模板专业化虚拟合法吗?
【发布时间】:2010-10-19 22:14:45
【问题描述】:

在 C++ 中,函数模板特化应该与普通函数完全一样。这是否意味着我可以制作一个虚拟的?

例如:

struct A
{
    template <class T> void f();
    template <> virtual void f<int>() {}
};

struct B : A
{
    template <class T> void f();
    template <> virtual void f<int>() {}
};

int main(int argc, char* argv[])
{
    B b;
    A& a = b;
    a.f<int>();
}

Visual Studio 2005 给我以下错误:

致命错误 C1001:编译器发生内部错误。

【问题讨论】:

  • 不错的错误信息!也许将其发布到 MS Connect,即使您的代码不“合法”,消息应该会更好。
  • 我不确定,所以我不会将此作为真正的答案发布,但我敢打赌这是不合法的,因为 vtable 在不同的编译中最终会有所不同使用不同类型调用函数的单元(或根本没有调用它)。
  • gcc 给出了大量错误消息,以“testtemp.cpp:4: error: explicit specialization in non-namespace scope `struct A'”开头
  • 这是不合法的,因为你不能专门化一个类定义。并且使模板虚拟也不起作用,因为模板不能是虚拟的。
  • 我在 MS Connect 上填写了一个错误:connect.microsoft.com/VisualStudio/feedback/…

标签: c++ templates virtual specialization c1001


【解决方案1】:

不错的编译器错误。对于这种类型的检查,我总是回退到Comeau 编译器,然后再回到标准并检查。

Comeau C/C++ 4.3.10.1(2008 年 10 月 6 日 11:28:09) 用于 ONLINE_EVALUATION_BETA2 版权所有 1988-2008 Comeau Computing。 版权所有。模式:严格 错误 C++ C++0x_extensions

“ComeauTest.c”,第 3 行:错误: 函数中不允许使用“虚拟” 模板 宣言 模板虚拟 void f(); ^

“ComeauTest.c”,第 10 行:错误: 函数中不允许使用“虚拟” 模板 宣言 模板虚拟 void f(); ^

现在,由于它已由另一个用户发布,事实是该标准不允许您定义虚拟模板化方法。基本原理是,对于所有虚拟方法,必须在 vtable 中保留一个条目。问题是模板方法只有在它们被实例化(使用)时才会被定义。这意味着 vtable 最终会在每个编译单元中具有不同数量的元素,具体取决于发生了多少对不同类型的 f() 的不同调用。然后地狱就会升起......

如果您想要的是一个基于其参数之一的模板化函数,并且一个特定版本是虚拟的(请注意参数的部分),您可以这样做:

class Base
{
public:
   template <typename T> void f( T a ) {}
   virtual void f( int a ) { std::cout << "base" << std::endl; }
};
class Derived : public Base
{
public:
   virtual void f( int a ) { std::cout << "derived" << std::endl; }
};
int main()
{
   Derived d;
   Base& b = d;
   b.f( 5 ); // The compiler will prefer the non-templated method and print "derived"
}

如果您希望将其概括为任何类型,那么您就不走运了。考虑另一种类型的委托而不是多态(聚合+委托可能是一种解决方案)。有关手头问题的更多信息将有助于确定解决方案。

【讨论】:

  • 请注意对 b.f(5) 的调用;将生成一个非虚拟的专业化并调用它。因为您使用非模板重载了模板,所以调用将首选非模板。但是如果你打电话给 b.f(5);它仍然会调用一个非虚函数。
  • 也就是说,重载当然是要走的路。事实上,在《c++ 编码标准》一书中,一条规则是避免函数模板显式特化,因为它是有限的,而重载提供了一种更好的选择。
  • 我正在尝试定义一个函数 GetValue,它将返回一个 T 类型的值。T 总是来自一组已知的类型。我希望能够从模板中调用函数,而不必为每个版本指定不同的名称。
  • 我现在的解决方法是使用“out”参数,以便重载分辨率可以匹配它,但我想知道它是否可能。
  • alexk7,一个简单的方法是类型到类型的包装器:virtual int getv(type_);虚拟布尔 getv(type_);而 type_ 是 template struct type_ { };并调用 b.getv(type_());现在,重载将弄清楚要调用什么,没有模板,但使用虚拟 :)
【解决方案2】:

根据http://www.kuzbass.ru:8086/docs/isocpp/template.html ISO/IEC 14882:1998:

-3- 成员函数模板不应是虚拟的。

例子:

template <class T> struct AA {
    template <class C> virtual void g(C);   //  Error
    virtual void f();                       //  OK
};

【讨论】:

    【解决方案3】:

    正如其他人所指出的,这不是合法代码,因为不能将成员函数模板声明为 virtual

    然而,即使是 Visual Studio 2012 也会对此感到窒息: Click here for full size

    事件日志表明编译器在0xC0000005STATUS_ACCESS_VIOLATION 上崩溃。有趣的是,某个(非法)代码构造如何使编译器出现段错误......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-26
      • 1970-01-01
      • 1970-01-01
      • 2011-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多