We know that C++ doesn't allow templated virtual function in a class. Anyone understands why such restriction?
Short answer: Virtual functions are about not knowing who called whom until at run-time, when a function is picked from an already compiled set of candidate functions. Function templates, OTOH, are about creating an arbitrary number of different functions (using types which might not even have been known when the callee was written) at compile-time from the callers' sides. That just doesn't match.
Somewhat longer answer: Virtual functions are implemented using an additional indirection (the Programmer's General All-Purpose Cure), usually implemented as a table of function pointers (the so-called virtual function table, often abbreviated "vtable"). If you're calling a virtual function, the run-time system will pick the right function from the table. If there were virtual function templates, the run-time system would have to find the address of an already compiled template instance with the exact template parameters. Since the class' designer cannot provide an arbitrary number of function template instances created from an unlimited set of possible arguments, this cannot work.
How would you construct the vtable? Theoretically you could have an infinite number of versions of your templated member and the compiler wouldn't know what they might be when it creates the vtable.
The other answers have already mentionned that virtual functions are usually handled in C++ by having in the object a pointer (the vptr) to a table. This table (vtable) contains pointer to the functions to use for the virtual members as well as some other things.
The other part of the explanation is that templates are handled in C++ by code expansion. This allow explicit specialization.
Now, some languages mandate (Eiffel -- I think it is also the case of Java and C#, but my knowledge of them is not good enough to be authoritative) or allow (Ada) an shared handling of genericity, don't have explicit specialization, but would allow virtual template function, putting template in libraries and could reduce the code size.
You can get the effect of shared genericity by using a technique called type erasure. This is doing manually what compilers for shared genericity language are doing (well, at least some of them, depending on the language, other implementation techniques could be possible). Here is a (silly) example:
#include <string.h>
#include <iostream>
#ifdef NOT_CPP
class C
{
public:
virtual template<typename T> int getAnInt(T const& v) {
return getint(v);
}
};
#else
class IntGetterBase
{
public:
virtual int getTheInt() const = 0;
};
template<typename T>
class IntGetter: public IntGetterBase
{
public:
IntGetter(T const& value) : myValue(value) {}
virtual int getTheInt() const
{
return getint(myValue);
}
private:
T const& myValue;
};
template<typename T>
IntGetter<T> makeIntGetter(T const& value)
{
return IntGetter<T>(value);
}
class C
{
public:
virtual int getAnInt(IntGetterBase const& v)
{
return v.getTheInt();
}
};
#endif
int getint(double d)
{
return static_cast<int>(d);
}
int getint(char const* s)
{
return strlen(s);
}
int main()
{
C c;
std::cout << c.getAnInt(makeIntGetter(3.141)) + c.getAnInt(makeIntGetter("foo")) << '\n';
return 0;
}
I think it's so that compilers can generate vtable offsets as constants (whereas references to non-virtual functions are fixups).
When you compile a call to a template function, the compiler usually just puts a note in the binary, effectively telling the linker "please replace this note with a pointer to the correct function". The static linker does something similar, and eventually the loader fills in the value once the code has been loaded into memory and its address is known. This is called a fixup, because the loader "fixes up" the code by filling in the numbers it needs. Note that to generate the fixup, the compiler doesn't need to know what other functions exist in the class, it just needs to know the munged name of the function it wants.
However with virtual functions, the compiler usually emits code saying "get the vtable pointer out of the object, add 24 to it, load a function address, and call it". In order to know that the particular virtual function you want is at offset 24, the compiler needs to know about all the virtual functions in the class, and what order they're going to appear in the vtable. As things stand, the compiler does know this, because all the virtual functions are listed right there in the class definition. But in order to generate a virtual call where there are templated virtual functions, the compiler would need to know at the point of the call, what instantiations there are of the function template. It can't possibly know this, because different compilation units might instantiate different versions of a function template. So it couldn't work out what offset to use in the vtable.
Now, I suspect that a compiler could support virtual function templates by emitting, instead of a constant vtable offset, an integer fixup. That is, a note saying "please fill in the vtable offset of the virtual function with this munged name". Then the static linker might fill in the actual value once it knows what instantiations are available (at the point where it removes duplicate template instantiations in different compilation units). But that would impose a serious burden of work on the linker to figure out vtable layouts, which currently the compiler does by itself. Templates were deliberately specified to make things easier on implementers, in the hope that they might actually appear in the wild some time before C++0x...
So, I speculate that some reasoning along these lines led the standards committee to conclude that virtual function templates, even if implementable at all, were too difficult to implement and therefore could not be included in the standard.
Note that there's a fair bit of speculation in the above even before I try to read the minds of the committee: I am not the writer of a C++ implementation, and nor do I play one on television.
Related
We can overload functions by giving them a different number of parameters. For example, functions someFunc() and someFunc(int i) can do completely different things.
Is it possible to achieve the same effect on classes? For example, having one class name but creating one class if a function is not called and a different class if that function is not called. For example, If I have a dataStorage class, I want the internal implementation to be a list if only add is called, but want it to be a heap if both add and pop are called.
I am trying to implement this in C++, but I am curious if this is even possible. Examples in other languages would also help. Thanks!
The type of an object must be completely known at the point of definition. The type cannot depend on what is done with the object later.
For the dataStorage example, you could define dataStorage as an abstract class. For example:
struct dataStorage {
virtual ~dataStorage() = default;
virtual void add(dataType data) = 0;
// And anything else necessarily common to all implementations.
};
There could be a "default" implementation that uses a list.
struct dataList : public dataStorage {
void add(dataType data) override;
// And whatever else is needed.
};
There could be another implementation that uses a heap.
struct dataHeap : public dataStorage {
void add(dataType data) override;
void pop(); // Maybe return `dataType`, if desired
// And whatever else is needed.
};
Functions that need only to add data would work on references to dataStorage. Functions that need to pop data would work on references to dataHeap. When you define an object, you would choose dataList if the compiler allows it, dataHeap otherwise. (The compiler would not allow passing a dataList object to a function that requires a dataHeap&.) This is similar to what you asked for, except it does require manual intervention. On the bright side, you can use the compiler to tell you which decision to make.
A downside of this approach is that changes can get messy. There is additional maintenance and runtime overhead compared to simply always using a heap (one class, no inheritance). You should do some performance measurements to ensure that the cost is worth it. Sometimes simplicity is the best design, even if it is not optimal in all cases.
Why does C++ RTTI require the class to have a virtual methods table? While it seems reasonable to use the table as a means for polymorphic upcasting, it doesn't seem like it is strictly required from a design point of view. For instance, the class could contain a hash or a unique identifier that conveys the information.
For the C++ experts who consider this question overly trivial, it would help the poster of this question, who is a humble beginner at C++, to provide an explanation of why vtables are required from a design point of view for RTTI, as well as what are the other design approaches (instead of using vtables) to implement RTTI (and why they work/don't work as well as vtables).
From a language perspective, the answer is: it doesn't. Nowhere in the C++ standard does it say how virtual functions are to be implemented. The compiler is free to make sure the correct function is called however it sees fit.
So, what would be gained by replacing the vptr (not the vtable) with an id and dropping the vtable? (replacing the vtable with an id doesn't really help anything whatsoever, once you have resolved vptr, you already know the run-time type)
How does the runtime know which function to actually call?
Consider:
template <int I>
struct A {
virtual void foo() {}
virtual void bar() {}
virtual ~A() {}
};
template <int I>
struct B : A<I> {
virtual void foo() {}
};
Suppose your compiler gives A<0> the ... lets call it vid ... 0 and A<1> the vid 1. Note that A<0> and A<1> are completely unrelated classes at this point. What happens if you say a0.foo() where a0 is an A<0>? At runtime a non-virtual function would just result in a statically dispatched call. But for a virtual function, the address of the function-to-call must be determined at runtime.
If all you had was vid 0 you'd still have to encode which function you want. This would result in a forest of if-else branches, to figure out the correct function pointer.
if (vid == 0) {
if (fid == 0) {
call A<0>::foo();
} else if (fid == 1) {
call A<0>::bar();
} /* ... */
} else if (vid == 1) {
if (fid == 0) {
call A<1>::foo();
} else if (fid == 1) {
call A<1>::bar();
} /* ... */
} /* ... */
This would get out of hand. Hence, the table. Add an offset that identifies the foo() function to the base of A<0>'s vtable and you have the address of the actual function to call. If you have a B<0> object on your hands instead, add the offset to that class' table's base pointer.
In theory compilers could emit if-else code for this but it turns out a pointer addition is faster and the resulting code smaller.
Vtables are a very efficient way of providing virtual functions. For the price of a single pointer per object, every member of the class can share the same static vtable.
Adding a second bunch of static information per class would require a second pointer per object. It's much easier to make the existing vtable pointer do double duty.
In the end it’s all down to history and trade offs.
On one side you need to be compatible with C, specifically standard layout types must have the same layout as in C, which means no place for RTTI.
On the other hand adding RTTI to a vtable will result in no size cost for the instance.
The designers of C++ decided to combine these two facts to the current implementation: only polymorphic types have dynamic RTTI information.
You can still obtain the static RTTI information and make your own layout for a non polymorphic type:
template<typename T>
struct S
{
const std::type_info &type = typeid(T);
T value;
};
You can even pass void pointers to value, they will have the same structure as T, and you know there is a type info pointer behind them.
After reading many posts about similar topics, and thinking about it for a while, still I do not understand why it is forbidden to implement template virtual functions.
The way I see it, this case has nothing to do with mixing static polymorphism with the dynamic one, but it is rather using template differentiation of the functions at the compile-time and then using dynamic polymorphism for each individual created function at the run-time.
Consider this piece of code:
class parrent{
public:
virtual float function(float value)const{
return value;
}
virtual double function(double value)const{
return value;
}
virtual long double function(long double value)const{
return value;
}
virtual ~parrent() = default;
};
class a_child:public parrent{
public:
float function(float value)const override{
return value + 1.5;
}
double function(double value)const override{
return value + 1.5;
}
long double function(long double value)const override{
return value + 1.5;
}
};
Obviously this code is OK and will achieve the expected result.
But using template to rewrite a similar code:
class parrent{
public:
template<typename t__>
virtual t__ function(t__ value)const{
return value;
}
virtual ~parrent() = default;
};
class a_child:public parrent{
public:
template<typename t__>
t__ function(t__ value)const override{
return value + 1.5;
}
};
Is not allowed.
I am not a compiler designer but from what I have read compilers will create a look up table from virtual functions and use them to launch the appropriate function at the run time, which is different from what they do in case of template functions. For any sets of template parameters given to use a template function at the compile time, compiler will create a unique function.
For this example compiler could detect template parameters in compile time simply by looking at how this virtual template function have been used throughout the entire program. Please consider the main function now:
int main() {
parrent* a;
parrent* b;
a = new parrent;
b = new a_child;
std::cout<< a->function(1.6f) << std::endl;
std::cout<< a->function(1.6) << std::endl;
std::cout<< a->function(1.6L) << std::endl;
std::cout<< b->function(1.6f) << std::endl;
std::cout<< b->function(1.6) << std::endl;
std::cout<< b->function(1.6L) << std::endl;
delete a;
delete b;
return 0;
}
Here Compiler will see that the function was used once for a float value, once for a double value and once for a long double value, so in any case it can easily create the right function with appropriate template parameters.
And in the end there will be 3 individual virtual functionS, not just one virtual function.
If we have a function which template parameters could not be deduced from the functions inputs like
template<typename t__>
virtual t__ function(int value){return value;}
Then users can just give the parameters themselves like:
object_pointer->function<double>(1234);
These practices are just what is already being used in case of any template functions, so why would it be different for virtual functions!
the only caveat to this practice that I can think of would be when the template virtual function get instantiated from a child object and never from the parent object or pointer.
Well even in that case same practice could be applied in order to create different virtual functions. Alternatively due to the lack of use of their virtuality they can become normal individual functions.
From the answer and comments it appears that there might be a serious problem with this approach which is obvious to every one else, so please be patient and help me understand it too.
I guess the mentioned problem in answers has something to do with compiler and/or linker not being able to know how many (and what type of) vtables it should produce for a class with regard to the rest of the codes or different translation units that it might face.
Well lets say it can produce an unfinished vtables list and extend it as it go along. The problem of ending up with two vtables or two different instances of a same class in the of case dynamic linking can already happen with instantiation of a template class with a virtual (non template)function.
So it seems that compilers already have a method to circumvent that problem!
First lets not forget that with regards to c, methods or class non static functions are nothing more than simple functions which require an object as one of their parameters, so lets not think of class as some intricate piece of code.
Second let's not get carried away by how compilers and linkers and what not works today. The language should be standard not the way compilers produce executable! Lets not forget that there are still many features in standard c++ 17 that even GCC does not cover yet!
Please explain to me in term of logic not the way compilers and/or linkers work what is the problem?
The way compilers implement polymorphic classes is as follows: the compiler looks at the class definition, determines how many vtable entries are needed, and statically assigns one entry in that vtable to each of the class's virtual methods. Wherever one of those virtual methods is called, the compiler generates code that retrieves the vptr from the class and looks up the entry at the statically assigned offset in order to determine the address that needs to be called.
We can now see how having a virtual template would cause issues. Suppose you had a class containing a virtual template. Now, after the end of the class definition, the compiler doesn't know how large to make the vtable. It has to wait until the end of the translation unit, to see the full list of the specializations of the template that are actually called (or to which a pointer-to-member is taken). If the class is only defined in this single translation unit, this problem could be solved by assigning vtable offsets to the template specializations in some increasing order in which they are encountered, then emitting the vtable at the end. However, if the class has external linkage, this breaks down, as when compiling different translation units, the compiler has no way of avoiding conflicts in the assignment of offsets to specializations of the virtual method template. Instead, the vtable offsets would have to be replaced with symbols that would be resolved by the linker once it has seen the list of referenced specializations from all translation units and merged them into a single list. It seems that if standard C++ required virtual templates to be supported, every implementation would have to require the linker to implement this functionality. I can guess that this will not be feasible any time soon.
I am not a compiler designer but I see a problem with what you are hoping to do.
When you have a virtual template member function, such as
template<typename t__>
virtual t__ function(t__ value)const{
return value;
}
there is no end to the types for which that is applicable. How does the compiler know whether to stop at int and double? There are unlimited number of types for which that function can be instantiated. Would you expect the compiler to generate vtable that takes into account all possible ways that function can be instantiated? That's infinite. It's not doable.
As I understand it, the compiler can inline a virtual function call when it knows at compile time what the type of the object will be at runtime (C++ faq).
What happens, however, when one is implementing a pure virtual method from a base class? Do the same rules apply? Will the following function call be inlined?
class base
{
public:
virtual void print() = 0;
virtual void callPrint()
{
print(); // will this be inline?
}
};
class child : public base
{
public:
void print() { cout << "hello\n"; }
};
int main()
{
child c;
c.callPrint();
return 0;
}
EDIT:
I think my original example code was actually a poor representation of what I wanted to ask. I've updated the code, but the question remains the same.
The compiler is never required to inline a function call. In this case, it is permitted to inline the function call, because it knows the concrete type of c (since it is not indirected through a pointer or reference, the compiler can see where it was allocated as a child). As such, the compiler knows which implementation of print() is used, and can choose not to perform vtable indirection, and further choose to inline the implementation of the function.
However, the compiler is also free to not inline it; it might insert a direct call to child::print(), or indirect through the vtable, if it decides to do so.
These optimizations in general boil down to the 'as-if' rule - the compiler must behave as-if it was doing a full vtable indirect - this means that the result must be the same, but the compiler can choose a different method of achieving the result if the result is the same. This includes inlining, etc.
The answer is of course "it depends", but in principle there's no obstruction to optimization. In fact, you're not even doing anything polymorphic here, so this is really straight-forward.
The question would be more interesting if you had code like this:
child c;
base & b = c;
b.print();
The point is that the compiler knows at this point what the ultimate target of the dynamic dispatch will be (namly child::print()), so this is eligible for optimization. (There are two separate opportunities for optimization, of course: one by avoiding the dynamic dispatch, and one coming from having the function body of the target visible in the TU.)
There are only a couple of rules you should be aware of:
1) The compiler is never forced to inline - even using the directive or defining a method in the header.
2) Polymorphism MUST ALWAYS WORK. This means that the compiler will prefer calling the function via the vftable rather than inlining it when the possibility of dynamic calls exists.
Suppose I have
class A { public: void print(){cout<<"A"; }};
class B: public A { public: void print(){cout<<"B"; }};
class C: public A { };
How is inheritance implemented at the memory level?
Does C copy print() code to itself or does it have a pointer to the it that points somewhere in A part of the code?
How does the same thing happen when we override the previous definition, for example in B (at the memory level)?
Compilers are allowed to implement this however they choose. But they generally follow CFront's old implementation.
For classes/objects without inheritance
Consider:
#include <iostream>
class A {
void foo()
{
std::cout << "foo\n";
}
static int bar()
{
return 42;
}
};
A a;
a.foo();
A::bar();
The compiler changes those last three lines into something similar to:
struct A a = <compiler-generated constructor>;
A_foo(a); // the "a" parameter is the "this" pointer, there are not objects as far as
// assembly code is concerned, instead member functions (i.e., methods) are
// simply functions that take a hidden this pointer
A_bar(); // since bar() is static, there is no need to pass the this pointer
Once upon a time I would have guessed that this was handled with pointers-to-functions in each A object created. However, that approach would mean that every A object would contain identical information (pointer to the same function) which would waste a lot of space. It's easy enough for the compiler to take care of these details.
For classes/objects with non-virtual inheritance
Of course, that wasn't really what you asked. But we can extend this to inheritance, and it's what you'd expect:
class B : public A {
void blarg()
{
// who knows, something goes here
}
int bar()
{
return 5;
}
};
B b;
b.blarg();
b.foo();
b.bar();
The compiler turns the last four lines into something like:
struct B b = <compiler-generated constructor>
B_blarg(b);
A_foo(b.A_portion_of_object);
B_bar(b);
Notes on virtual methods
Things get a little trickier when you talk about virtual methods. In that case, each class gets a class-specific array of pointers-to-functions, one such pointer for each virtual function. This array is called the vtable ("virtual table"), and each object created has a pointer to the relevant vtable. Calls to virtual functions are resolved by looking up the correct function to call in the vtable.
Check out the C++ ABI for any questions regarding the in-memory layout of things. It's labelled "Itanium C++ ABI", but it's become the standard ABI for C++ implemented by most compilers.
I don't think the standard makes any guarantees. Compilers can choose to make multiple copies of functions, combine copies that happen to access the same memory offsets on totally different types, etc. Inlining is just one of the more obvious cases of this.
But most compilers will not generate a copy of the code for A::print to use when called through a C instance. There may be a pointer to A in the compiler's internal symbol table for C, but at runtime you're most likely going to see that:
A a; C c; a.print(); c.print();
has turned into something much along the lines of:
A a;
C c;
ECX = &a; /* set up 'this' pointer */
call A::print;
ECX = up_cast<A*>(&c); /* set up 'this' pointer */
call A::print;
with both call instructions jumping to the exact same address in code memory.
Of course, since you've asked the compiler to inline A::print, the code will most likely be copied to every call site (but since it replaces the call A::print, it's not actually adding much to the program size).
There will not be any information stored in a object to describe a member function.
aobject.print();
bobject.print();
cobject.print();
The compiler will just convert the above statements to direct call to function print, essentially nothing is stored in a object.
pseudo assembly instruction will be like below
00B5A2C3 call print(006de180)
Since print is member function you would have an additional parameter; this pointer. That will be passes as just every other argument to the function.
In your example here, there's no copying of anything. Generally an object doesn't know what class it's in at runtime -- what happens is, when the program is compiled, the compiler says "hey, this variable is of type C, let's see if there's a C::print(). No, ok, how about A::print()? Yes? Ok, call that!"
Virtual methods work differently, in that pointers to the right functions are stored in a "vtable"* referenced in the object. That still doesn't matter if you're working directly with a C, cause it still follows the steps above. But for pointers, it might say like "Oh, C::print()? The address is the first entry in the vtable." and the compiler inserts instructions to grab that address at runtime and call to it.
* Technically, this is not required to be true. I'm pretty sure you won't find any mention in the standard of "vtables"; it's by definition implementation-specific. It just happens to be the method the first C++ compilers used, and happens to work better all-around than other methods, so it's the one nearly every C++ compiler in existence uses.