I have got two classes.
The first class (A) is builded with an template.
template <class T>
class A
{
public:
T value;
};
The second class (B) should have an object of class A as member variable. Like this:
class B
{
public:
A<int> value;
};
But now i want to use any kind of template-class in class A. Not only int.
Apparent I can't declare a (member-)variable which contains any kind of a class.
So, I need something like this:
class B
{
public:
A<*> value;
};
Is there any (clean) solution for this problem?
-- Greeting from Germany, Bastian
You cannot have a single class B with "any" member object, because B has to be a well-defined class, and A<T> is a different type for different types T. You can either make B a template itself:
template <typename T>
class B
{
A<T> value;
};
or you can take a look at boost::any, which is type-erasing container for arbitrary types (but making use of it requires a certain amount of extra work). The any class only works for value types, though, it's not completely arbitrary.
The simplest solution would be to make all A variants ineherit from a common interface, even if it's empty :
class IA{}
template <class T>
class A : public IA
{
public:
T value;
};
class B
{
public:
IA* value;
};
Now, the associated costs:
interactions with value are limited to the IA interface;
if you try to cast to get the real type, that mean that you know the real type, so it's of no use and make A type a parameter of B becomes really easier to use.
there are runtime costs associated to runtime inheritance
Advantage :
it's easily understood by other developers
it naturally limit the types possible to some specific ones
it don't use boost (sometimes, you just can't)
So to do better there are other less simple solutions but that are simple enough to be used :
If you can use boost, boost::any, boost::variant and boost::mpl might be base of solutions.
Boost any can be used as a safe replacement to void*. The only problem with this is that you can have ANY type, like if the type was a template parameter of the B class.
Boost variant might be used successfully if you know all the types that A can be.
MPL might be helpful if you just want to set a list of possible types and make sure your members apply only to them. You can do a ton of things with MPL so it really depends on your exact needs.
You've got two choices, I think. The first is to parameterize your class over the type parameters of the instance variables:
template <class T> struct B
{
A<T> value;
};
The other option is to declare value as a void* pointer. (But that's probably not what you want).
yes, it's already been done. boost::any.
I think it helps to understand, that templated classes create an entirely new and seperate class for every type you use with it. For instance, Vector<int> and Vector<float> are as separate as the classes VectorInt and VectorFloat.
For class B, you are basically asking that the value variable either be A<int> or A<float>, which is the same as saying you want value to either be a "A_int" or "A_float". And to accomplish that you... well, use another template!
Related
Say an Object has a templated TYPE (for a very good reason)
template <typename T> class Object {
};
I want an Object to be able to contain an array of subobjects.
template <typename T> class Object {
vector< Object<T>* > subobjects;
};
However, the subobjects CAN BE OF ANY TYPE. That is, a subobject can be an Object of a different T than it’s parent.
The language is enforcing a restriction that doesn’t need to be there. How do I fix this?
A template is a type-factory. Object<int> and Object<double> are unrelated types.
Object<int> generates one type, and Object<double> generates a different type.
If you want these two types to be related, you can make them related.
struct IObject {
virtual ~IObject() {}
// common interface
};
then
template<class T>
struct Object<T>:IObject {
std::vector<IObject*> subObjects;
};
now your Object<T> has subobjects. All the code knows (statically) is that they are IObjects.
Now, if you want the type T to be runtime determined, you could use std::any (this has type safety issues, sort of; it is "brittle" in that the reader needs to know the exact type that went into it) or std::variant<double, int, std::string> (but here you have to name all of the supported types).
Basically, you are probably using a round peg and a square hole.
You have a problem. You thought templates where the solution. You found a problem with your solution. You looked for a way to make templates work.
The language is enforcing a restriction that doesn’t need to be there. How do I fix this?
The language isn't enforcing anything. You wrote a template with one type parameter, so that parameter can only take one value at a time.
However, the subobjects CAN BE OF ANY TYPE. That is, a subobject can be an Object of a different T than it’s parent.
If you mean each Object<T> can have children of a single different type U != T, just write
template <typename T, typename U> class Object {
vector< Object<U>* > subobjects;
};
If you mean each object can have children of a variety of instantiations, you either need some form of type erasure (eg. std::any), or polymorphism.
In either case, you need to give some thought to what you're going to do with these things you don't know the concrete type of. How will you use them? This is the information needed to choose the appropriate mechanism.
Using #WayneVanderLinde's comment:
struct Object{
vector<Object*> subobjects;
};
template<typename T> struct TObject : public Object {};
you can now create
TObject<int> i;
TObject<double>* d = new TObject<double>();
i.subobjects.push_back( d );
In Java if you have a class like:
class Box<E>
{
some code
}
you can do the following using a wildcard:
Box<?> someBox;
someBox = new Box<Integer>();
someBox = new Box<Double>();
Is there a way to do this in C++?
In better words, how can I declare a variable in C++ that can hold either Box<Integer> or Box<Double> or Box<WhateverDataTypeHere>?
template <typename T> class Box should inherit from a non-template base (let's say class BasicBox).
Then a pointer to BasicBox can point to objects of the specializations of the derived template:
BasicBox *someBox = new Box<int>;
Or, since in modern C++™ manually managing memory should be avoided, using a smart pointer would be a better idea:
std::unique_ptr<BasicBox> someBox = std::make_unique<Box<int>>();
In addition to HolyBlackCat's excellent answer, I should also mention you do have a couple of other options to achieve a similar effect.
If you have some subset of classes, you can inherit them all from the same base like this:
class Base {
};
class Derived1 : public Base {
};
class Derived2 : public Base {
};
Then, you can create a Box like this:
Box<std::uinque_ptr<Base>> genericBox;
genericBox can now hold any derived, though because of the way C++ works, you probably need to hold it by pointer, reference, or std::unique_ptr. Consequently, this is kind of messy, but works.
Also, I should mention that using this method will not work for types like int or double, so this is probably not useful if you need to use those types.
Better is, if you have access to it, to use std::variant. This will allow you to store a specific set of types:
Box<std::variant<int, double>> genericBox;
This genericBox can hold either a double or an int.
If you don't have direct access to std::variant, there is also a boost::variant which is basically the same thing. Or, if you have access to neither, you do have the option of a union:
union GenericType {
int myInterger;
double myDouble;
};
Box<GenericType> genericBox;
Of course, std::variant and boost::variant is better, but this will work for you if you're ever in a situation where you don't have those.
Now, these options will only work if you ever know ahead of time what types you are attempting to store. If you don't know that, you have the option of std::any:
Box<std::any> genericBox;
As with std::variant, there is also a boost::any. You can also implement it yourself if you really have to for some crazy reason. But I wouldn't recommend doing that unless you are doing it for educational purposes or something.
In C++ it's called templating
template <typename T>
class Box {
private:
T boxcontents;
}
replace T with whatever type you want. Here's some great documentation on the topic
Well, this is not something that someone want but you can use a pointer for basic types like int
For classes you can do that or use some base class for all types you may use.
If I have a template class, that I want to instantiate with different data types:
template <typename T>
class A {
T value;
// ...
};
And I also want to use the objects of this class in a Standard Template Library container (say vector).
In my understanding creating a vector of A objects would not be accepted by the compiler, because A<int> and A<char> are actually different types and I can't put them in the same vector.
The workaround I found was creating a base class, a derived template class, and a vector of base class pointers.
class ABase {
// ...
};
template <typename T>
class ADerived : public ABase{
T value;
// ...
};
std::vector<BaseA*> mySuperVector;
I am starting to experiment with templates to gain a better understanding and I am wondering whether there are better solutions for this. My workaround above gives me also headache, because I am afraid that typecasting will be inevitable at some point.
Templates are a compile-time code generation construct. If you need an heterogeneous container of objects at compile-time, then you can use std::tuple:
std::tuple my_tuple{A<int>{}, A<char>{}, A<double>{}};
If you need an heterogeneous container of objects at run-time, you do need some sort of polymorphism. Using a base class with virtual methods is a valid option. If you know all the possible choice of types your object can be in advance, you can also use std::variant:
using my_a = std::variant<A<int>, A<char>, A<double>>;
std::vector<my_a> vec;
In this case, my_a can either be A<int>, A<char>, or A<double> at any given time. The active alternative can change at run-time.
Needing to typecast to the derived type is not related to the derived type being the instantiation of a class template, it's a design issue. Maybe you need virtual functions or multiple containers instead.
Otherwise, your solution is fine, but do switch to std::unique_ptr if the container is supposed to own the contained objects.
I am working on a fairly tightly coupled library which up until now has explicitly assumed all computations are done with doubles. I'm in the process of converting some of the core classes to templates so that we can start computing with std::complex<double>. I've templated about 10 of our classes so far have noticed a tendency toward proliferation of templates. As one class becomes templated, any other class that uses the templated class appears to need templating as well. I think I can avoid some of this proliferation by defining abstract base classes for my templates so that other classes can just use pointers to the abstract class and then refer to either a double or std::complex<double> version of the derived class. This seems to work on at the header level, but when I dive into the source files, the templated class will often have functions which compute a value or container of values of type double or std::complex<double>. It seems like a waste to template a whole class just because a couple of lines in the source file are different because of some other classes return type.
The use of auto seems like a possible way to fix this, but I'm not 100% sure it would work. Suppose I have an abstract base class AbstractFunction from which Function<Scalar> derives, where Scalar can be double or std::complex<double>. Now suppose we have two member functions:
virtual Scalar Function<Scalar>::value(double x);
virtual void Function<Scalar>::values(std::vector<Scalar> &values, std::vector<double> x);
And suppose I have some other class (that I don't want to template) with a member function that calls one of these.
// populate double x and std::vector<double> xs
auto value = functionPtr->value(x);
std::vector<auto> values;
functionPtr->values(values, xs);
// do something with value and values
where functionPtr is of type std::shared_ptr<AbstractFunction>.
I could see auto working for the first case, but I don't believe I could construct a vector of auto to be filled with the second one. Does this necessitate making the calling class a template? Can someone recommend another strategy to cut down on the proliferation of templates?
I think you are already wrong in assuming that the first use-case is going to work. If you have an abstract base class, then either value is a member of it and you can call it through std::shared_ptr<AbstractFunction> or value is not a member of it and only available if you know the derived class' type. In the first case, the AbstractFunction::value method must have a fixed return type, it can not depend on Scalar, which is the template parameter of the derived class.
That said: In my experience the two concept often don't mix well. You either want to create an abstract base class with the full interface or you want a template. In the latter case, there is often no need / no benefit for having an abstract base class. It then follows that also the code using your template works with templates.
What might help you is to "export" the template parameter from Function, i.e.
template<typename T>
class Function
{
public:
using value_type = T;
value_type value() const;
// ...
};
and in other parts of the code, use a template which takes any T which behaves like Function if you don't want to directly write out (and limit yourself) to Function:
template<typename T>
void something( const std::shared_ptr<T>& functionPtr )
{
// ignoring where x comes from...
using V = typename T::value_type;
V value = functionPtr->value(x);
std::vector<V> values;
functionPtr->values(values, xs);
}
Note that this is just one option, I don't know if it is the best option for your use-case.
If I have a mix-in defined as...
template<class T> class Mixin : public T
{
// mixin methods and members
};
...and declare it with T being a non-polymorphic class...
Mixin<NonPoly> mixin;
..and then have a base class pointer to it...
NonPoly* nonPolyPtr = &mixin;
...how can I later ensure nonPolyPtr is pointing to a Mixin type?
dynamic_cast<Mixin*>(nonPolyPtr)
The above does not compile because the base class is non-polymorphic.
I saw Boost has some trait classes that may help, but I'm hoping there's a simpler solution I'm overlooking.
I think you are looking at the wrong requirements. You don't need to do any casting here, but you may need to do some restructuring of your code. If you have a relationship of classes A, which creates mixin and B which uses NonPoly, then just pass B the NonPoly pointer and use the mixin directly in A. There should be no reason to give up the type information in A just to try to get it back again. If there are more classes, separate them into those who know the mixin and those who know NonPoly, and it's the same relationship.
And it is very likely that if this is the case in the first place, a mixin design is not the proper approach. Very often, mixins are used when simple containment is needed. In my example with A and B above, you may have a Mixin class
template <typename T>
class Mixin
{
T * GetObject()
{ return & t_; }
// other methods that use t_
private:
T t_;
};
and then just pass the object when it needs to be operated on. Or even more common, if you are just passing T to some 3rd party library, you need no mixin at all. Containment might not even be best. The best way to maintain encapsulation is always to write file-scope algorithms when you can manipulate the type T through it's public interface and public 3rd party routines.
If you can explain why you think you need to lose the type information and then later recover, we might be able to show more clearly how you can restructure ownership so that doesn't need to happen, but since this type information never leaves the runtime (since you are looking to cast - your question implies it's not getting serialised or anything), I can assure you that there is some design where that type information is not lost in the first place.
If you are certain of its type just use static_cast to downcast. You also need to specify the template parameter in the cast Mixin<NonPoly>*.
template<class T>
class Mixin : public T
{
// mixin methods and members
};
class NonPoly {
};
int main() {
Mixin<NonPoly> mixin;
NonPoly* nonPolyPtr = &mixin;
Mixin<NonPoly>* mixinPtr = static_cast<Mixin<NonPoly>*>(nonPolyPtr);
}