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.
Related
When I create a templated class with a virtual function, and override the function in a derived class, the base function still tries to get compiled.
Here is the minimal reproducible example:
#include <iostream>
template<typename T>
class Base
{
public:
virtual void Method()
{
static_assert(false);
}
};
class Derived : public Base<int>
{
public:
void Method() override
{
std::cout << "Hello, World!";
}
};
int main()
{
Derived d{};
d.Method();
return 0;
}
Why does the base method still try to compile when it is overriden?
First, it needs to be compiled as user can instantiate Base<T> and directly call Method() on it. If you wanted to make it non-callable, use virtual void Method() = 0; for abstract functions.
Second, not only compiled, it's actually accessible from Derived: you can call it e.g. from Derived::Method(), as Base<int>::Method().
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.
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.
Lets say we have following hierarchy:
class Abstract
{
public:
virtual void foo() = 0;
};
class Base : public Abstract
{
public:
virtual void foo() override; //provides base implementation
};
class Derived : public Base
{
public:
virtual void foo() override; //provides derived implementation
};
If Base::foo() is ever called on the Derived object that object will desync and its data will be corrupted. It inherits Base's data structure and its manipulation but needs to perform additional operations so calling only the Base::foo() will omit these extra operations and as a result the Derived's state will be corrupted.
Therefore I would like to prevent direct call of Base implementation of foo so this:
Derived d;
d.Base::foo();
ideally, should give me a compile time error of some sorts. Or do nothing or otherwise be prevented.
However it might be I am violating the polymorphism rules and should use composition instead but that would require a lots of extra typing...
How about template method pattern:
class Abstract
{
public:
void foo() { foo_impl(); }
private:
virtual void foo_impl() = 0;
};
class Base : public Abstract
{
private:
virtual void foo_impl() override; //provides base implementation
};
class Derived : public Base
{
private:
virtual void foo_impl() override; //provides derived implementation
};
then
void test(Abstract& obj) {
obj.foo(); // the correct foo_impl() will be invoked
}
Derived d;
test(d); // impossible to call the foo_impl() of Base
You can explore the template method pattern. It allows for greater control of the execution of the methods involved.
class Abstract
{
public:
virtual void foo() = 0;
};
class Base : public Abstract
{
protected:
virtual void foo_impl() = 0;
public:
//provides base implementation and calls foo_impl()
virtual void foo() final override { /*...*/ foo_impl(); }
};
class Derived : public Base
{
protected:
virtual void foo_impl() override; //provides derived implementation
};
The pattern is seen in the iostreams library with sync() and pubsync() methods.
To prevent the direct calls and maintain the consistent state, you will need to get the final implementation of the foo method in the correct place in the stack. If the intent is to prohibit the direct call from the top of the hierarchy, then you can move the _impl methods up.
See also the non-virtual interface, the NVI pattern.
Bear in mind as well that the overriding methods do not have to have the same access specifier as the Abstract class. You could also just make the methods in the derived classes private or protected;
class Abstract
{
public:
virtual void foo() = 0;
};
class Base : public Abstract
{
virtual void foo() override; //provides base implementation
};
class Derived : public Base
{
virtual void foo() override; //provides derived implementation
};
Note: unless otherwise intended, changing the access specifier could be considered bad design - so basically if you do change the access specifier, there should should be a good reason to do so.
You can make all the foo() methods non-public, then have a non-virtual function in the Abstract class that simply calls foo.
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.