Why explicit template arguments are required when overriding virtual functions? - c++

Here's my problem. I got the following error if I don't put template argument explicitly in derived::getType() function.
class template argument deduction is only available with -std=c++1z or -std=gnu++1z
But even with -std flag, it still fires error.
class myT {
// full implementation
};
template<class T>
class base {
public:
virtual my_getter_type<T>* getter() = 0;
}
class derived : public base<myT> {
public:
my_getter_type<myT>* getter(); // this is OK, no compile error
my_getter_type* getType(); // this is NOT OK, compile error
}

You're overthinking it. The return type must be a type, obviously. my_getter_type<myT>* is a type. my_getter_type is a template.
my_getter_type* doesn't make sense either, because to form a pointer type the * has to follow a base type.

There is no inherent connection between the return type of a function in a base class and the return type of a function with the same name in a derived class. That is, it's perfectly legal to do this:
struct base {
int f();
};
struct derived : base {
double f();
};
Making f() virtual in the base doesn't change that.
Same thing with the return type of getter() in the question; the base class version returns my_getter_type<T>, but that does not mean that the derived version must also return my_getter_type<T>; again, it would be perfectly legal for the derived version of getter() to return my_getter_type<T*>, int, void, or anything else.
And, of course, my_getter_type is a template, not a type, so cannot be a legal return type.

Related

How to return a templated derived class instead of base class in an overridden function?

#include <iostream>
template <typename T>
class A {};
template <typename T>
class B : public A<T> {};
class C
{
virtual A<int> func() = 0;
};
class D : public C
{
virtual B<int> func() override
{
return B<int>{};
}
};
Here B inherits from A and the template argument is int for both of the return values. Why can't I just return B<int> instead of A<int>?
As merlinND pointed out, you have to inherit from class A<T>. But it will still fail unless you return a pointer or reference to B instead of a value type. The following will compile.
#include <iostream>
template <typename T>
class A {};
template <typename T>
class B : public A<T> {};
class C
{
virtual A<int>* func() = 0;
};
class D : public C
{
virtual B<int>* func() override
{
return new B<int>{};
}
};
When declaring B, you must inherit from an actual class, not a template:
template <typename T>
class B : public A<T> {};
The second issue is that you cannot change the signature of func() when overriding it in class D. However, once that's fixed, you will be able to return B<int>{} as expected.
Edit: a third problem pointed out by #Josh Wilson is that you will not get the behavior of a B from the return value of func() because it specifies an A returned by value. You need to return a pointer to A<int> for polymorphism to apply.
B D::func() cannot be an override of AC::func(), because the return type of those two function are different from each other.
From cppreference:
If the function Derived::f overrides a function Base::f, their return types must either be the same or be covariant.
There are 3 conditions to be all satisfied for two types to be covariant, and one of them is that they need to be either a pointer type or a reference type.
A<int> and B<int> are neither the same type nor covariant types, which implies that you cannot use B<int> as the return type of your overridden method.
Please also notice that you don't have to use virtual specifier in your overridden method.
Moreover, as also other people noticed, the following will work and return an A<int>-typed object:
A<int> func() override
{
return B<int>{};
}
But you should avoid it. In fact, after a B<int> object is created, it will be sliced into an A<int> type, for the reasons we have seen, meaning your object type will effectively be simply A<int>, and not a B<int>'s instance. This means that you uselessly created a B<int> instance when you could have just created an A<int>.
If you need a B<int>, you'll need to return either a pointer or a reference.

Why static_cast conversion from base to derived works inside base but not outside

Why static_case conversion from base class to derived works inside base class, but doesn't work outside the base class
#include <iostream>
using std::cout;
class Base
{
public:
template <typename T>
int getValue() const { return static_cast<const T&>(*this).getValue(); }
};
class Derived: public Base
{
public:
Derived(int v): value(v) { }
int getValue() const { return value; }
int value;
};
class Another
{
int getValue() const { return 5; }
};
template <typename T>
void out(const Base & base) {
cout << base.getValue<T>() << '\n';
}
int main() {
Derived d(5);
Base b;
out<Derived>(d); //understandable, d has derived part.
out<Derived>(b); //don't understand, b is only base.
out<Another>(b); //compile time error
//static_cast<Derived>(b); //compile time error
}
I read this article about CRTP and stumble upon this code:
template <typename T>
class Base
{
public:
void doSomething()
{
T& derived = static_cast<T&>(*this);
use derived...
}
};
class Derived : public Base<Derived>
{
...
};
And I do not clearly understand how conversion works here too.
The static_cast conversion shall be used only if this conversion is legal. In your code you are creating an object of class Base, and you are trying to convert it to the class Derived. Luckily to you the implementation of the Derived::getValue() doesn't use any data members, and returns a value from literal. Anyway that is undefined behavior.
In case of CRTP no instance of Base class is created: only instances of the Derived are used.
Upd. Try this:
//static_cast<Derived>(b); doesn't compile
static_cast<Derived&>(b); shall compile
Upd 2. You get junk because the Derived::getValue() uses a data member (in your initial version of code data members were not used).
This is part of the rules of C++. static_cast can be used to convert a base class expression to derived class. If , at runtime, the object is not actually a base class subobject of a derived class object then it is undefined behaviour with no diagnostic required.
The first sentence of your question is incorrect, this cast can be written at any point of the code.
out<Another>() fails to compile because Another is not related by inheritance to Base.
Last cast in main() is incorrect syntactically and not equivalent to code in template, you can't upcast object to object (you can downcast through, causing type contraction). In templates above you cast references.
Derived& can be bound to Base&, static_cast have no way to check it. CRTP ensures it, because this point at storage of Derived type, *this results in reference that can be safely cast to Derived& reference object.
Reference to Another can't be bound to reference to Base, when Base is not base class of Another. In that case casting pointers or references using static_cast is illegal.
Template code is legal, in case of CRTP works because template code is instatiated where Derived is comlete enough type, i.e. where template was used. Template itself doesn't generate anything and isn't compiled, only checked for correctness.
Still, in CRTP some things won't be possible, e.g. to use inside of Base class nested type declarations from Derived class as complete types, for a simple reason : they are not complete and are not subject for forward lookup, unlike member variables and functions. If such use required, then a third type has to be defined before Base, contsining required declarations.

I can't override Base class's method because my derived class is templatised

I try to override the base class function but because I have templatised its child/derived/sub class I can't override the function depending on which type I instantiate the template with.
struct BaseType
{
virtual double getPropertyValue() = 0;
};
template <typename T>
struct NumberType : BaseType
{
T propertyValue;
T getPropertyValue() override { return propertyValue; } // I would like this to return whatever T is.
};
int main()
{
std::vector<BaseType*> vectr;
NumberType<double> doubleType; // This is fine, overrides the base function
NumberType<int> intType; // Has no overrider, won't compile
}
So I thought maybe I could templatise also the Base class, so that the base class function returns T also. But if I do this I won't be able to keep them in a container or point to them, because they'll all be of different Base< type> types.
I also thought about templatising Base and having it inherit from an even higher parent (which isn't templatised), but I run into the same problem.
Is there any way around this?
You can do this if the return value T is covariant with the return value of the pure virtual function. But sadly a T will not, in general, be covariant with a double.
Accepting that you're mixing up static and dynamic polymorphism techniques, which might be a design flaw, you could define
struct Cov {};
with
struct BaseType
{
virtual Cov& getPropertyValue() = 0;
};
Then,
template <typename T>
struct NumberType : BaseType
{
T propertyValue;
T& getPropertyValue() override { return propertyValue; }
};
where T is a child class of Cov: this relationship means that T& is a return type that's covariant with Cov&, and so compilation will succeed. Doing it this way also obviates a value copy of T being taken. You might also find it convenient to build conversion operators for the various Ts that end up getting built that have primitive type return values.
You can also introduce const reference return types to suit the exact requirements.
BaseType has a contract that is binding for all its descendants. It says says that getPropertyValue returns double. NumberType doesn't get to modify the contract, it's set in stone.
Let's pretend the contract is not there.
BaseType& base = BaseContainer.getSome();
// base can be NumberType<complex> or
// or NumberType<tensor<double,4,4,4>> or NumberType<padic<5>>
// or a type that is not even thought of yet
// as of now.
what = base.getPropertyValue(); // how should 'what' be declared?
There's no way to use the result of base.getPropertyValue() if we don't know what it is.
Making the return type covariant doesn't really help. It just shifts the problem from BaseType to whatever base class BaseType::getPropertyValue() returns.
You need to come up with a usable interface of BaseType and stick to it in all descendant classes.

Prevent writing clone method for each subclass

Here is my case:
class A
{
public:
virtual A* clone() = 0;
};
class B : public A
{
virtual B* clone() override
{
return new B();
}
};
In my code now 100% of sublcasses of A just implement the clone method exactly the same way only for class D I have return type D and I create object of D of course.
How can I prevent this duplication? What patterns or tricks can help?
I have thought to use CRTP pattern, but it uses templates, and my function is virtual. As we understand templates and virtual functions are incompatible.
NVI pattern does not work either, as return type of clone methods are different.
[class.virtual]/8:
If the class type in the covariant return type of D::f differs from
that of B::f, the class type in the return type of D::f shall be
complete at the point of declaration of D::f or shall be the class
type D.
So CRTP cannot work perfectly, since the template argument which refers to the derived class would be an incomplete type, and thus covariant return types are difficult to simulate.
However, here's a try:
class A
{
public:
virtual A* clone() = 0;
};
namespace detail {
template <typename T>
struct Cloner : A {
using A::A; // For constructors
virtual A* clone() override {return new T;}
};
}
template <typename T>
struct Cloner : detail::Cloner<T> {
using detail::Cloner<T>::Cloner; // For constructors
// For callers
template <class=void>
T* clone() {return static_cast<T*>(detail::Cloner<T>::clone());}
};
class B : public Cloner<B>
{
};
Demo.
Note two things:
The overload of clone returning a pointer to the derived class type is a function template. That's so we don't override the virtual function clone: If we would, the code would be ill-formed, because the return type is not covariant (see above).
Because clone is a function template, to ensure that it get's called at all, we can to let it hide the virtual clone function. That's achieved via inheritance: Names in derived classes hide names in base classes. Thus if we call through a B-pointer/reference, we get the appropriate return type (B*), since the name is looked up in ::Cloner before detail::Cloner.

C++ - Use inferred template argument from constructor as template argument throughtout class

I'd like to use the inferred type from the constructor argument as template arguments throughout the rest of my class. Is this possible?
Something like this:
class AnyClass
{
public:
template<Class C>
AnyClass(C *c) {
//class C is inferred by constructor argument
}
// constructor argument is used as argument in other template types
nestclass<C> myNestedClass;
void randomfunction(C *randonarg) {
}
}
Details:
Here's the thing. I'm trying to initialize my base type given the type of the inheriting class. In the case below DerivedA inherits from Base, but DerivedB inherits from DerivedA, therefore from what I understand the value of this in the constructor of Base (found in DerivedA) will actually be a pointer to DerivedB therefore the inferred type in Base will be of type DerivedB. However I'd like to use this type throughout the rest of my Base class and not just limit it to the constructor.
class Base {
// type T derived from inheriting class
template<T>
Base(T *) {};
//like to use other places
void randomfunction(T *arg1) {
//does something with type T
};
}
class DerivedA : publicBase {
DerivedA() : Base(this) { //this should be a pointer to DerivedB, since it is inherited
//from DerivedB.
}
}
class DerivedB : class DerivedA {
//anything
}
**My main goal is to use the inheriting class type in my base class. I realize this is sort of a different qst, but I was thinking my solution would be found somehow in my original question.
I'm thinking using a go-between method (similar to the function proposed below) but not sure if it will work.
Thanks for the help!
No.
But you can make your entire class a template class, and create a factory function that does type deduction to generate your template class.
template<typename C>
class AnyClass {
public:
AnyClass( C* c );
std::vector<C> vec;
};
AnyClass<C> make_any_class( C* c ) {
return AnyClass<C>(c);
};