I have the following template classes,
I need to figure out how to implement a conversion operator between the derived template classes.
template<class T>
class Base
{
public:
Base () { }
template <class U>
operator Base<U>()
{
return Base<U> (v);
}
virtual double getV() = 0;
};
class D1: public Base<D1>
{
public:
D1(int j)
{
i = j;
}
double getV() const { return i; }
template <class U>
operator Base<U>()
{
return Base<U>(getV());
}
private:
int i;
};
class D2: public Base<D2>
{
public:
D2(int j)
{
i2 = j;
}
double getV() const { return i2; }
template <class U>
operator Base<U>()
{
return Base<U>(getV());
}
private:
int i2;
};
How can I achieve the following?
D1 d1(3);
D2 d2 = d1; //conversion from 'D1' to non-scalar type 'D2' requested
if the design itself sound or should I be doing something else?
Please let me know what ur thoughts
In your example, I don't see a reason why CRTP is used.
The specializations of Base all have a virtual member function that is not dependent on the template parameter. Your code suggests this virtual member function can be used to access all data necessary to create an instance of any class derived from a specialization of Base. If we follow this assumption, one could rather think of:
class Base
{
public:
virtual double getV() const = 0;
};
class D1 : public Base
{
int i;
public:
D1(int);
virtual double getV() const { return i; }
};
class D2 : public Base
{
int i;
public:
D2(int);
virtual double getV() const { return i; }
};
This still does not allow conversions. However, it is quite simple to add them here:
class D1 : public Base
{
int i;
public:
D1(int);
D1(Base const& p) : D1(p.getV()) {}
virtual double getV() const { return i; }
};
This conversion should be allowed by D1 and not by Base, because only D1 knows what data is required for it to be constructed.
If CRTP is necessary for things not shown here, you could still use a common base class:
class Common_base
{
public:
virtual double getV() const = 0;
};
template<class T>
class Base : public Common_base
{
};
class D1 : public Base<D1>
{
int i;
public:
D1(int);
D1(Common_base const& p) : D1(p.getV()) {}
virtual double getV() const { return i; }
};
If, for some reason, the CRTP is required for the conversion, you could still use a converting constructor template:
template<class T>
class Base
{
public:
virtual double getV() const = 0; // whyever
};
class D1 : public Base
{
int i;
public:
D1(int);
template<class U>
D1(Base<U> const& p) : D1(p.getV()) {}
virtual double getV() const { return i; }
};
It's hard to tell what you are trying to do but I don't think you can do it as described.
If you really want to have a common interface, you need to declare a common base class that includes all the methods and properties that your code needs, and have both classes derive from that. You can then always cast to the base class.
This seems ideal in fact. Especially since you can always use virtual methods to customize behavior in the base class if needed.
If that doesn't work within your current task, perhaps you should talk more about why you need this.
You either write a constructor for D2 that takes a const D1& or you write a conversion operator in D1 that returns a D2. You have to decide what it means to do this conversion and implement the conversion appropriately.
You can add a constructor in your derived classes that takes a Base template :
template <class U>
D2(const Base<U> &other)
{
i2 = other.getV();
}
But not sure if it will fit your needs.
Related
Is it possible to declare some type of base class with template methods which i can override in derived classes? Following example:
#include <iostream>
#include <stdexcept>
#include <string>
class Base
{
public:
template<typename T>
std::string method() { return "Base"; }
};
class Derived : public Base
{
public:
template<typename T>
std::string method() override { return "Derived"; }
};
int main()
{
Base *b = new Derived();
std::cout << b->method<bool>() << std::endl;
return 0;
}
I would expect Derived as the output but it is Base. I assume it is necessary to make a templated wrapper class which receives the implementing class as the template parameter. But i want to make sure.
1) Your functions, in order to be polymorphic, should be marked with virtual
2) Templated functions are instantiated at the POI and can't be virtual (what is the signature??How many vtable entries do you reserve?). Templated functions are a compile-time mechanism, virtual functions a runtime one.
Some possible solutions involve:
Change design (recommended)
Follow another approach e.g. multimethod by Andrei Alexandrescu (http://www.icodeguru.com/CPP/ModernCppDesign/0201704315_ch11.html)
Template methods cannot be virtual. One solution is to use static polymorphism to simulate the behavior of "template virtual" methods:
#include <iostream>
#include <stdexcept>
#include <string>
template<typename D>
class Base
{
template<typename T>
std::string _method() { return "Base"; }
public:
template<typename T>
std::string method()
{
return static_cast<D&>(*this).template _method<T>();
}
};
class Derived : public Base<Derived>
{
friend class Base<Derived>;
template<typename T>
std::string _method() { return "Derived"; }
public:
//...
};
int main()
{
Base<Derived> *b = new Derived();
std::cout << b->method<bool>() << std::endl;
return 0;
}
where method is the interface and _method is the implementation. To simulate a pure virtual method, _method would absent from Base.
Unfortunately, this way Base changes to Base<Derived> so you can no longer e.g. have a container of Base*.
Also note that for a const method, static_cast<D&> changes to static_cast<const D&>. Similarly, for an rvalue-reference (&&) method, it changes to static_cast<D&&>.
Another possible aproach to make your example work as you expect is to use std::function:
class Base {
public:
Base() {
virtualFunction = [] () -> string { return {"Base"}; };
}
template <class T> string do_smth() { return virtualFunction(); }
function<string()> virtualFunction;
};
class Derived : public Base {
public:
Derived() {
virtualFunction = [] () -> string { return {"Derived"}; };
}
};
int main() {
auto ptr = unique_ptr<Base>(new Derived);
cout << ptr->do_smth<bool>() << endl;
}
This outputs "Derived". I'm not sure that this is what you realy want, but I hope it will help you..
I had the same problem, but I actually came up with a working solution. The best way to show the solution is by an example:
What we want(doesn't work, since you can't have virtual templates):
class Base
{
template <class T>
virtual T func(T a, T b) {};
}
class Derived
{
template <class T>
T func(T a, T b) { return a + b; };
}
int main()
{
Base* obj = new Derived();
std::cout << obj->func(1, 2) << obj->func(std::string("Hello"), std::string("World")) << obj->func(0.2, 0.1);
return 0;
}
The solution(prints 3HelloWorld0.3):
class BaseType
{
public:
virtual BaseType* add(BaseType* b) { return {}; };
};
template <class T>
class Type : public BaseType
{
public:
Type(T t) : value(t) {};
BaseType* add(BaseType* b)
{
Type<T>* a = new Type<T>(value + ((Type<T>*)b)->value);
return a;
};
T getValue() { return value; };
private:
T value;
};
class Base
{
public:
virtual BaseType* function(BaseType* a, BaseType* b) { return {}; };
template <class T>
T func(T a, T b)
{
BaseType* argA = new Type<T>(a);
BaseType* argB = new Type<T>(b);
BaseType* value = this->function(argA, argB);
T result = ((Type<T>*)value)->getValue();
delete argA;
delete argB;
delete value;
return result;
};
};
class Derived : public Base
{
public:
BaseType* function(BaseType* a, BaseType* b)
{
return a->add(b);
};
};
int main()
{
Base* obj = new Derived();
std::cout << obj->func(1, 2) << obj->func(std::string("Hello"), std::string("World")) << obj->func(0.2, 0.1);
return 0;
}
We use the BaseType class to represent any datatype or class you would usually use in a template. The members(and possibly operators) you would use in a template are described here with the virtual tag. Note that the pointers are necessary in order to get the polymorphism to work.
Type is a template class that extends Derived. This actually represents a specific type, for example Type<int>. This class is very important, since it allows us to convert any type into the BaseType. The definition of the members we described described in BaseType are implemented here.
function is the function we want to override. Instead of using a real template we use pointers to BaseType to represent a typename. The actual template function is in the Base class defined as func. It basically just calls function and converts T to Type<T>. If we now extend from Base and override function, the new overridden function gets called for the derived class.
I've got class with template and some classes inherit from him.
We want to create instance of the father class without declaring its template type, and call a function returning the template type.
Example:
class FatherWrap {
virtual ~FatherWrap() = default;
};
template<typename T>
class FatherClass : public FatherWrap
{
virtual T getValue();
};
class SonClass1 : public FatherClass<int>
{
int getValue() override;
};
class SonClass2 : public FatherClass<string>
{
string getValue() override;
};
int main()
{
FatherWrap* ch = new SonClass1();
T a = ch->getValue; // What to do instead of T.
}
Let's say you have:
An unparameterized (no template) base class B
An intermediate template class I<T> inheriting from B
Some derived classes D1, D2, etc. each of which inherits from a specialization of I
You want to write some code in terms of B. You can do that--but you have to limit yourself to using the API that B defines. The methods of B can be virtual, and the implementations/overrides of those methods in I<T> and Dx can use the type T, but those types can't be exposed to a component the only knows about B.
If you want to write some logic that uses T, then that logic needs to be either in a method of I<T>, or in a template function that is itself parameterized with a class type:
template<class U>
U someTypeSpecificLogic(I<U> intermediate) {
// can call methods that accept/return U here
}
You can't write logic in terms of B that depends on the type T because that type is only defined for the subclass I<T>. Consider that the B you have be a different subclass of B and not an I<T> at all.
You could skip the FatherWrap entirely and make the base class return a variant:
struct FatherClass : FatherWrap {
virtual std::variant<int, std::string> getValue();
};
struct SonClass1 : FatherClass {
std::variant<int, std::string> getValue() override {
return "some text";
}
};
struct SonClass2 : FatherClass {
std::variant<int, std::string> getValue() override {
return 95;
}
};
Alternatively, you could template any code that uses SonClass:
struct SonClass1 { // no parent.
std::string getValue() {
return "some text";
}
};
struct SonClass2 { // no parent.
int getValue() {
return 95;
}
};
template<typename T>
void useSonClass(T& son) {
// value may be int or string.
auto value = son.getValue();
}
int main() {
SonClass1 sc1;
SonClass2 sc2;
useSonClass(sc1);
useSonClass(sc2);
}
If you want to contain it, just use a variant:
int main() {
std::variant<SonClass1, SonClass2> sc = SonClass2{};
std::visit(
[](auto& sc) { useSonClass(sc); },
sc
);
}
As far as I know, templated virtual functions aren't allowed/possible due to the undefined size of the vtable.
On the other hand, virtual functions inside a class template which don't use the template type seem to be allowed, right?
What about a virtual function that doesn't use the template type as parameter or return type but works on data of the template type? Would that be valid C++?
I have already done some testing and it seems to work.
My Code looks like this:
(Note: For reasons of readability this is only the basic structure, not the real code).
template<typename T>
class Base {
public:
virtual bool compare(void) {
// Basic implementation
return ((value1 + value2) < value3);
}
protected:
T value1, value2, value3;
}
/**
* Derived from Base<ComplexClass> where
* ComplexClass is a Class providing
* a int Value through .getInt()
**/
class Derived : Base<ComplexClass> {
bool compare(void) {
return ((value1.getInt() + value2.getInt()) < value3.getInt());
}
}
main {
Base<int> *intBase = new Base<int>();
Base<double> *doubleBase = new Base<double>();
Base<ComplexClass> *complexBase = new Derived();
intBase->compare(); // Should call base function
doubleBase->compare(); // Should also call base function
complexBase->compare(); // Should call the compare function of Derived
}
As far as i can tell this works like I excepted. Is this just a lucky coincidence or is this valid/good C++ style?
If it's valid, could someone please explain what's happening inside and why some people say it's forbidden/bad practice to derive from class templates and use virtual functions inside of class templates?
Thank you in advance!
PS: I know something similar could have been done by template specialization but I'd like to know if it's also possible this way.
Q As far as I know, templated virtual functions aren't allowed/possible due to the undefined size of the vtable.
A You can have virtual function in class templates.
Example code that compiles and links:
template <typename T>
struct Base
{
virtual T doSomething(T const& in) = 0;
Base(T const& data) : data_(data) {}
T data_;
};
struct Concrete : public Base<int>
{
Concrete(int d) : Base(d) {}
virtual int doSomething(int const& in)
{
return data_*in;
}
};
int main()
{
Concrete a(20);
int b = a.doSomething(10);
}
Q On the other hand, virtual functions inside a class template which don't use the template type seem to be allowed, right?
A The virtual functions of a class template can use anything -- not restricted to not using the template tye.
My example should make that clear.
Q What about a virtual function that doesn't use the template type as parameter or return type but works on data of the template type? Would that be valid C++?
A Yes, it will.
Again, my example should make that clear.
EDIT: Extended example
template <typename T>
struct Base
{
virtual T fun1(T const& in) = 0;
virtual T fun2(int in) = 0;
virtual int fun3(T const& in) = 0;
virtual int fun4(int in) = 0;
Base(T const& data) : data_(data) {}
T data_;
};
struct Concrete : public Base<int>
{
Concrete(int d) : Base(d) {}
virtual int fun1(int const& in)
{
return data_*in;
}
virtual int fun2(int in)
{
return fun1(in);
}
virtual int fun3(int const& in)
{
return fun1(in);
}
virtual int fun4(int in)
{
return fun1(in);
}
};
int main()
{
Concrete a(20);
int b = a.fun1(10);
int c = a.fun2(10);
int d = a.fun3(10);
int e = a.fun4(10);
}
This is perfectly valid. However, here you can have the same behaviour with specialization or just overloading, e.g.
template<typename T>
struct Base
{
bool compare() const { return val(value1) + val(value2) < val(value3); }
protected:
T value1, value2, value3;
private:
template<typename U>
static U val(U a) { return a; }
static int val(const ComplexClass& a) { return a.getInt(); }
};
Better keep virtual functions for when it's really needed.
And try to gather as much as possible shared code in a single place, minimizing what is to be specialized.
Is it possible to declare some type of base class with template methods which i can override in derived classes? Following example:
#include <iostream>
#include <stdexcept>
#include <string>
class Base
{
public:
template<typename T>
std::string method() { return "Base"; }
};
class Derived : public Base
{
public:
template<typename T>
std::string method() override { return "Derived"; }
};
int main()
{
Base *b = new Derived();
std::cout << b->method<bool>() << std::endl;
return 0;
}
I would expect Derived as the output but it is Base. I assume it is necessary to make a templated wrapper class which receives the implementing class as the template parameter. But i want to make sure.
1) Your functions, in order to be polymorphic, should be marked with virtual
2) Templated functions are instantiated at the POI and can't be virtual (what is the signature??How many vtable entries do you reserve?). Templated functions are a compile-time mechanism, virtual functions a runtime one.
Some possible solutions involve:
Change design (recommended)
Follow another approach e.g. multimethod by Andrei Alexandrescu (http://www.icodeguru.com/CPP/ModernCppDesign/0201704315_ch11.html)
Template methods cannot be virtual. One solution is to use static polymorphism to simulate the behavior of "template virtual" methods:
#include <iostream>
#include <stdexcept>
#include <string>
template<typename D>
class Base
{
template<typename T>
std::string _method() { return "Base"; }
public:
template<typename T>
std::string method()
{
return static_cast<D&>(*this).template _method<T>();
}
};
class Derived : public Base<Derived>
{
friend class Base<Derived>;
template<typename T>
std::string _method() { return "Derived"; }
public:
//...
};
int main()
{
Base<Derived> *b = new Derived();
std::cout << b->method<bool>() << std::endl;
return 0;
}
where method is the interface and _method is the implementation. To simulate a pure virtual method, _method would absent from Base.
Unfortunately, this way Base changes to Base<Derived> so you can no longer e.g. have a container of Base*.
Also note that for a const method, static_cast<D&> changes to static_cast<const D&>. Similarly, for an rvalue-reference (&&) method, it changes to static_cast<D&&>.
Another possible aproach to make your example work as you expect is to use std::function:
class Base {
public:
Base() {
virtualFunction = [] () -> string { return {"Base"}; };
}
template <class T> string do_smth() { return virtualFunction(); }
function<string()> virtualFunction;
};
class Derived : public Base {
public:
Derived() {
virtualFunction = [] () -> string { return {"Derived"}; };
}
};
int main() {
auto ptr = unique_ptr<Base>(new Derived);
cout << ptr->do_smth<bool>() << endl;
}
This outputs "Derived". I'm not sure that this is what you realy want, but I hope it will help you..
I had the same problem, but I actually came up with a working solution. The best way to show the solution is by an example:
What we want(doesn't work, since you can't have virtual templates):
class Base
{
template <class T>
virtual T func(T a, T b) {};
}
class Derived
{
template <class T>
T func(T a, T b) { return a + b; };
}
int main()
{
Base* obj = new Derived();
std::cout << obj->func(1, 2) << obj->func(std::string("Hello"), std::string("World")) << obj->func(0.2, 0.1);
return 0;
}
The solution(prints 3HelloWorld0.3):
class BaseType
{
public:
virtual BaseType* add(BaseType* b) { return {}; };
};
template <class T>
class Type : public BaseType
{
public:
Type(T t) : value(t) {};
BaseType* add(BaseType* b)
{
Type<T>* a = new Type<T>(value + ((Type<T>*)b)->value);
return a;
};
T getValue() { return value; };
private:
T value;
};
class Base
{
public:
virtual BaseType* function(BaseType* a, BaseType* b) { return {}; };
template <class T>
T func(T a, T b)
{
BaseType* argA = new Type<T>(a);
BaseType* argB = new Type<T>(b);
BaseType* value = this->function(argA, argB);
T result = ((Type<T>*)value)->getValue();
delete argA;
delete argB;
delete value;
return result;
};
};
class Derived : public Base
{
public:
BaseType* function(BaseType* a, BaseType* b)
{
return a->add(b);
};
};
int main()
{
Base* obj = new Derived();
std::cout << obj->func(1, 2) << obj->func(std::string("Hello"), std::string("World")) << obj->func(0.2, 0.1);
return 0;
}
We use the BaseType class to represent any datatype or class you would usually use in a template. The members(and possibly operators) you would use in a template are described here with the virtual tag. Note that the pointers are necessary in order to get the polymorphism to work.
Type is a template class that extends Derived. This actually represents a specific type, for example Type<int>. This class is very important, since it allows us to convert any type into the BaseType. The definition of the members we described described in BaseType are implemented here.
function is the function we want to override. Instead of using a real template we use pointers to BaseType to represent a typename. The actual template function is in the Base class defined as func. It basically just calls function and converts T to Type<T>. If we now extend from Base and override function, the new overridden function gets called for the derived class.
Following does not work:
std::vector<IRule*> vec;
RuleRangeDouble *rule = new RuleRangeDouble(0, 100);
vec.push_back(rule);
Now how can a make a vector of different rules? I know, I have to use pointers... But what else do I have to do to get this working? How can I change my base structure to get this work?
I use an Interface like the following:
// Interface
template <typename T>
class IRule
{
public:
virtual bool isValid(T value) = 0;
};
And my example class looks like this:
class RuleRangeDouble : public IRule<double>
{
private:
double min;
double max;
public:
bool isValid(double value)
{
....
};
};
The vector needs to be a vector of an actual type, for example std::vector<IRule<double>*>. Irule on its own is not a type, it is a class template. So you would need
std::vector<IRule<double>*> vec;
RuleRangeDouble *rule = new RuleRangeDouble(0, 100);
vec.push_back(rule);
If the template parameter is not part of the interface, you can introduce a common base class. Don't forget to give it a virtual destructor:
class IRule
{
public:
virtual bool isValid(T value) = 0;
virtual ~IRule() {}
};
template <typename T>
class Rule : public IRule
{
.....
};
class RuleRangeDouble : public Rule<double>
{
....
};
Then your original use case sample would work:
std::vector<IRule*> vec; // IRule really is a type now
RuleRangeDouble *rule = new RuleRangeDouble(0, 100);
vec.push_back(rule);
You can implement something like getBestValidValue()in several ways. One is to define a special general (but non-template) return type to be used for getBestValidValue() and also as argument type for isValid(value) (juanchopanza's answer was faulty here):
class ValueType
{
enum { is_int, is_double } my_type; // add more if you want
union { my_int; my_double; }; // union only works for simple types
public:
// implicit construction from allowed types
ValueType(int x) : my_type(is_int), my_int(x) {}
ValueType(double x) : my_type(is_double), my_double(x) {}
// use SFINAE for is<type>() function
template<typename T>
typename std::enable_if<std::is_same<T,int>::value,bool>::type
is() const { return my_type==is_int; }
template<typename T>
typename std::enable_if<std::is_same<T,double>::value,bool>::type
is() const { return my_type==is_double; }
// implicit type conversion to allowed types
// note: does not assert(is<T>())
operator int() { return my_int; }
operator double() { return my_double; }
};
class IRule
{
public:
virtual bool isValid(ValueType const&) const = 0; // fixed bug in juanchopanza's answer
virtual ValueType getBestValidValue() const = 0; // return any type of value
virtual ~IRule() {}
};
template<typename T>
class Rule : public IRule
{
protected:
virtual bool valid(T) const = 0;
virtual T bestValidValue() const = 0;
public:
bool isValid(ValueType const&value) const
{
return value.is<T>()
&& valid(value); // implicit type conversion to T
}
ValueType getBestValidValue() const
{
return bestValidValue(); // implicit construction of ValueType
}
....
}