16

In C++, a function template specialization is supposed to act exactly like a normal function. Does that mean that I can make one virtual?

For example:

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 gives me the following error:

fatal error C1001: An internal error has occurred in the compiler.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
alexk7
  • 2,721
  • 1
  • 21
  • 18
  • 8
    Nice error message! Maybe post it to MS Connect, even if your code is not "legal", the message should be better. – Lucero Apr 16 '09 at 17:47
  • 3
    I don't know for sure, so I'm not posting this as a real answer, but I'd bet that it is not legal, since the vtable would end up being different in different compilation units that called the function with different types (or didn't call it at all). – rmeador Apr 16 '09 at 17:49
  • gcc gives a plethora of error messages, starting with "testtemp.cpp:4: error: explicit specialization in non-namespace scope `struct A'" – veefu Apr 16 '09 at 17:56
  • it can't be legal, because you can't specialize within a class definition. and making the template virtual won't work either, because templates can't be virtual. – Johannes Schaub - litb Apr 16 '09 at 18:26
  • I filled a bug on MS Connect: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=432999 – alexk7 Apr 16 '09 at 19:15

3 Answers3

24

Nice compiler error. For this type of checks I always fallback to the Comeau compiler before going back to the standard and checking.

Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for ONLINE_EVALUATION_BETA2 Copyright 1988-2008 Comeau Computing. All rights reserved. MODE:strict errors C++ C++0x_extensions

"ComeauTest.c", line 3: error: "virtual" is not allowed in a function template declaration template virtual void f(); ^

"ComeauTest.c", line 10: error: "virtual" is not allowed in a function template declaration template virtual void f(); ^

Now, as it has been posted by another user, the fact is that the standard does not allow you to define virtual templated methods. The rationale is that for all virtual methods, an entry must be reserved in the vtable. The problem is that template methods will only be defined when they have been instantiated (used). This means that the vtable would end up having a different number of elements in each compilation unit, depending on how many different calls to f() with different types happen. Then hell would be raised...

If what you want is a templated function on one of its arguments and one specific version being virtual (note the part of the argument) you can do it:

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"
}

If you want this generalized for any type, then you are out of luck. Consider another type of delegation instead of polymorphism (aggregation + delegation could be a solution). More information on the problem at hand would help in determining a solution.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 3
    please note a call to b.f(5); will generate a specialization that's not virtual and call it. Because you overloaded the template with a non-template, a call will prefer the non-template. but if you call b.f(5); it will still call a non-virtual function. – Johannes Schaub - litb Apr 16 '09 at 18:43
  • 1
    that said, of course overloading is the way to go here. in fact, in the book "c++ coding standards", one rule is to avoid function template explicit specialization, because it's limited and overloading provides a superior alternative. – Johannes Schaub - litb Apr 16 '09 at 18:45
  • I'm trying to define a function GetValue that will return a value of type T. T is always from a known set of types. I want to be able to call the function from templates without having to give each version a different name. – alexk7 Apr 16 '09 at 18:56
  • My workaround now is to use a "out" parameter so that overload resolution can match it but I wanted to know if it was possible. – alexk7 Apr 16 '09 at 18:57
  • 2
    alexk7, an easy way is a type-to-type wrapper: virtual int getv(type_); virtual bool getv(type_); while type_ is template struct type_ { }; and call with b.getv(type_()); now, overloading will figure out what to call, without templates, but with virtual :) – Johannes Schaub - litb Apr 16 '09 at 18:59
  • Thanks for the idea! I can still use the template in the base class to hide the use of type_: template T getv() { return getv(type_()); } – alexk7 Apr 16 '09 at 19:13
  • Does anybody know if any update on this topic in C++20? – Jarek C Oct 29 '20 at 12:41
4

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

-3- A member function template shall not be virtual.

Example:

template <class T> struct AA {
    template <class C> virtual void g(C);   //  Error
    virtual void f();                       //  OK
};
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
veefu
  • 2,820
  • 1
  • 19
  • 29
1

As others have noted, this is not legal code because a member function template cannot be declared virtual.

Yet even Visual Studio 2012 chokes on this: C++ internal compiler error on Visual Studio 2012 Click here for full size

Event logs indicate that the compiler crashed on 0xC0000005, or STATUS_ACCESS_VIOLATION. It's funny how a certain (illegal) code construct can make the compiler segfault...

bwDraco
  • 2,514
  • 2
  • 33
  • 38