Consider the following code. Is it guaranteed that Derived<int>::foo() will be instantiated? foo() is virtual and is called by a non-virtual function of the base class.
#include <iostream>
class Base
{
public:
void bar() { foo(); }
private:
virtual void foo() = 0;
};
template <typename T> class Derived: public Base
{
public:
Derived(T t_) : t(t_) {}
private:
void foo() override { std::cout << t; }
T t;
};
Derived<int> make_obj()
{
return Derived<int>(7);
}
Standard section 14.7.1/11 says
It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated.
However, for a typical vtable implementation, instantiating any constructor of the class requires a vtable for the class to exist, which must contain a pointer to the specialization's virtual function definition. So in practice the virtual function will probably be instantiated.
Virtual table will always be instantiated for a class hierarchy, however, in your case it will be compiler dependent whether the foo gets actually initialized on a class creation since the class itself is initialized on a stack and never used polymorphocally. The virtual table will be meaningless in your case.
Related
Consider:
#include <iostream>
class Base
{
public:
virtual void foo() { std::cout << "Base::foo()\n"; };
};
class Derived : public Base
{
public:
void foo() override
{
std::cout << "Derived::foo()\n";
Base::foo();
}
};
int main()
{
Derived obj;
obj.foo();
return 0;
}
This is my code. Why can I call Base::foo() in the Derived class if I already redefined it in Derived class. Why doesn't the compiler delete Base::foo in class Derived after redefine?
"why compiler doesn't delete Base::foo in class Derived after redefine"
Because that isn't what virtual and override do. When you provide an override to a base class function, you do not replace it. You are defining a new version for that function. The base class's implementation continues to exist and to be accessible.
Consider the following code. Someone can still use a Base object, and the behaviour should not be changed because Derived exists. The output for base_obj.foo() should continue to be "Base::foo()" regardless of the existance of Derived. :
#include <iostream>
class Base
{
public:
virtual void foo() { std::cout << "Base::foo()\n"; }
};
class Derived : public Base
{
public:
void foo() override { std::cout << "Derived::foo()\n"; }
};
int main()
{
Derived obj;
obj.foo();
Base base_obj;
base_obj.foo();
return 0;
}
Also consider that multiple classes can derive from Base. I could add a class MyClass : public Base with its own version of foo(), and it should not interfere with how Base or Derived objects behave.
If overriding a member function would cause the base member function to be entirely replaced or removed, it becomes nearly impossible to reason about code without reading carefully every class that derives from it. And unless your IDE provides tools for that, it implies reading all of the code base. It would make it C++ code that uses polymorphism extremely difficult to understand.
The following code illustrates my question:
namespace foo1
{
class bar1
{
public:
virtual void fn() = 0; //this must remain pure virtual
};
};
namespace foo2
{
class bar2
{
public:
virtual void fn() = 0; //this must remain pure virtual
};
};
class bar3: public foo1::bar1, public foo2::bar2
{
public:
//can i separately override virtual functions from inherited
//classes that have the same name?
void foo1::bar1::fn() override {std::cout << "bar1";} //error
void foo2::bar2::fn() override {std::cout << "bar2";} //error
};
int main()
{
bar3* obj = new bar3();
((foo1::bar1*)obj)->fn(); //i want this to print "bar1"
((foo2::bar2*)obj)->fn(); //and this to print "bar2"
}
Basically I want to be able to override inherited functions, such that by simply casting my base class object to one of the inherited classes, I can call the different functions even though they have the same name. Is this possible?
Sure.
struct foo1bar1helper:public foo1::bar1{
void fn()final{foo1bar1fn();}
virtual void foo1bar1fn()=0;
};
now bar3 just inherits from this instead of bar1 and overrides foo1bar1fn(),
Do the same with bar2.
You can also use CRTP to dispatch and do away with the extra vtable lookup;
template<class D>
struct foo1bar1helper:public foo1::bar1{
void fn()final{static_cast<D*>(this)->foo1bar1fn();}
};
template<class D>
struct foo2bar2helper:public foo2::bar2{
void fn()final{static_cast<D*>(this)->foo2bar2fn();}
};
class bar3:
public foo1bar1helper<bar3>,
public foo2bar2helper<bar3>
{
public:
void foo1bar1fn(){}
void foo2bar2fn(){}
};
You can not do that. When you do foo1::bar1::fn(), compiler thinks that you are defining function fn of 'bar1' of 'foo1' namespace in class bar3. Hence compiler is throwing below error.
error: cannot define member function ‘foo1::bar1::fn’ within ‘bar3’
But when you extend from an abstract class, you need to give the definition of the function (which should be part of your derived class) in derived class.
Expected this code to compile and work
template<class T>
class Base
{
virtual void method() = 0;
};
template<>
void Base<int>::method() { std::cout << "overrided" << std::endl; }
Base<int> base;
But it gives the 'Base<int>': cannot instantiate abstract class error. Thought partial specialization would make Base<int> non-abstract and allow to instantiate it.
Is there a working solution as short as this one and that keeps the Base class abstract? Otherwise I can make the Base class non-abstract or use Nicol Bolas's solution from here: Template specialization and inheritance
If it won't work for a non-template class, why should it work for a template class?
#include<iostream>
class Base
{
virtual void method() = 0;
};
void Base::method() { std::cout << "overrided" << std::endl; }
Base base;
errors:
10 : error: cannot declare variable 'base' to be of abstract type 'Base'
Base base;
^
3 : note: because the following virtual functions are pure within 'Base':
class Base
^
8 : note: virtual void Base::method()
void Base::method() { std::cout << "overrided" << std::endl; }
^
Compilation failed
What about specialization of whole class (instead of just one member function):
template<class T>
struct TempClass
{
virtual void f() = 0;
};
template <>
struct TempClass<int>
{
virtual void f()
{
//...
}
};
Note that TempClass<int> is no longer abstract class, but other Base classes are still abstract classes, (TempClass<float>, TempClass<double>, TempClass<SomeClassType>, ...).
and
it won't contain fields that generic class TempClass contains. You will have to copy-paste them from generic Base or, which is more clever solution,
you'll create base class with fields that both specialization have and then make those template classes inherit from that base class:
template <typename T>
struct Base
{
// some members that all Base classes have
};
template <typename T>
struct TempClass: Base<T>
{
virtual void f() = 0;
};
template <>
struct TempClass<int>: Base<int>
{
virtual void f()
{
//...
}
};
This way ugly copy-paste wasn't needed.
It is possible to provide an implementation of a pure virtual function in a class. That does not make the class instantiable.
class Base
{
virtual void method() = 0;
};
void Base::method() { /* Do something */ }
// This is still a problem since Base
// is still an abstract class, i.e. it is not still not
// instantiable.
Base base;
Given the code below, why the compiler throws an error (saying that the method calc() is not member of Model2) when using Sub<Model2> instance, if the fn() is declared virtual, and works when the fn() is not virtual? What is going on?
class Model1
{
public:
void calc(){std::cout<<"Model1 calc"<<std::endl;}
};
class Model2
{
public:
void calc2(){std::cout<<"Model2 calc"<<std::endl;}
};
template<typename T>
class Super : public T
{
public:
virtual void fn() // comment virtual for resolution
{ T::calc(); }
};
template<typename T>
class Sub : public Super<T>
{
public:
void fn()
{ T::calc2(); }
};
int main()
{
Super<Model1> bes;
bes.fn();
Sub<Model2> sts1;
sts1.fn();
return 0;
}
virtual methods have to be instantiated in a template, whereas non-virtual methods don't.
Errors depending of T "requirement" happen at the moment of instantiation.
Non-virtual methods are instantiated only when used or explicit instantiated.
In your case, when not virtual, Super<Model2>::fn is never called (it is Sub<Model2>::fn).
In the virtual case, Super<Model2>::fn is not called, but has to be instanciated because it is virtual.
Early binding for template and late binding for virtual function. Therefore, is it safe if a template contains virtual function?
template<typename T>
class base {
public:
T data;
virtual void fn(T t){}
};
It is completely safe. Once you instantiate the class template, it becomes normal class just like other classes.
template<typename T>
class base {
public:
T data;
virtual void fn(T t){}
};
class derived : base<int> {
public:
virtual void fn(int t){} //override
};
base<int> *pBase = new derived();
pBase->fn(10); //calls derived::fn()
I would also like to point out that while it is allowed virtual function in a class template, it is not allowed virtual function template inside a class (as shown below):
class A
{
template<typename T>
virtual void f(); //error: virtual function template is not allowed
};
Yes, it's quite safe. You'd use it by having a class derive from it:
class derived : public base<int> {
virtual void fn(int) { std::cout << "derived"; }
};
Of course, if it contains any other virtual functions (i.e., is intended to be used as a base class) you generally want to make the dtor virtual as well.
There is no safety concern associated with virtual function inside a template class. It is as good as, having a virtual function inside a normal class.