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.
Related
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.
I have a base class that uses template, and it has a few methods that are not dependent on the template type, but when I use the pointer Base* a instead of the derived class the compiler complains because there is no type specified. I know in java this is possible but not sure if it is possible in C++. Here a simple example:
template <typename T>
class Base {
public:
Base(const T& t) : _t(t) {}
virtual ~Base() { }
void doSomething() { std::cout << "Hello world/n"; }
virtual T getVal() const { return _t; }
private:
T _t;
};
class DerivedA : public virtual Base<std::string>
{
public:
DerivedA(const std::string& path) : Base<std::string>(path) {}
virtual ~DerivedA() {}
};
class DerivedB : public virtual Base<int>
{
public:
DerivedB(int value) : Base<int>(value) {}
virtual ~DerivedB() {}
};
int main(int argc, const char * argv[]) {
DerivedA d("hello world\n");
Base* basePtr = &d; // ERROR: Use of class template 'Base' requires template arguments
basePtr->doSomething();
Thanks in advance
Simply create another base class which is not a template:
class ReallyBase {
public:
virtual ~ReallyBase() = default;
void doSomething() { std::cout << "Hello world\n"; }
};
template <typename T>
class Base : public ReallyBase {
public:
Base(const T& t) : _t(t) {}
virtual const T& getVal() const { return _t; }
private:
T _t;
};
Generics in java are something completely different than templates in C++. Generics are based on type-erasure while with templates each instantiation is a seperate type completely unrelated to a different instantiation.
Ie in Java an ArrayList<Integer> is basically the same thing as a ArrayList<Boolean>. With templates on the other hand, Base<int> has no relation to Base<std::string> (other than being two types resulting from the same template) and hence your derived classes actually do not share a common base class.
To solve your problem you could write a (non-template) base class that declares the common interface of all your derived classes.
Let's 1st talk about a simple solution to your problem, writing a derivation function:
template<typename T>
Base<T>* make_base(Base<T>* param) {
return param;
}
You can use this as follows:
DerivedA d("hello world\n");
auto basePtr = make_base(&d);
basePtr->doSomething();
This solution can be further improved as in this example but since I don't think it's the best solution I'll not clutter the answer with it but leave such optimization for your perusal.
But 2nd let's talk about improving your design. You'll notice that any of the standard containers create a using value_type =... thereby storing the template types that they are passed. This is important for this exact situation! If you publicly add using value_type = T to your Base template class you'll circumvent the problem you're having all together, without the need for a make_base you'll be able to do:
Base<decltype(d)::value_type>* basePtr = &d;
An important follow up concept here is that DerivedA and DerivedB do not have the same base class. Thus you can never do something like this
auto basePtr = make_base(&d);
basePtr = new DerivedB({});
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.
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.