In C++, suppose I have a templatized class, like
template <typename T>
class Foo
{
...
};
and suppose that I have several kinds of Foo objects, like
Foo<int> intFoo = Foo<int>();
Foo<double> doubleFoo = Foo<double>();
...
and so on.
Actually, its worse than that. I really want an intFoo object of a class that inherits from Foo< int >, for instance.
I would like to do something like this:
std::vector<Foo<?> > aVector;
aVector.push_back(intFoo);
aVector.push_back(doubleFoo);
Keeping in mind that I have substantially oversimplified my design case, is there a simple way to do this?
A solution would be to have your Foo inheriting from an empty base class
struct CommonBase {};
template<typename T>
class Foo : public CommonBase
{
// ...
};
and then have a container of pointers to the common base
vector<CommonBase*> v;
If you want to keep away from inheritance, you could use boost:any to store any type in your container.
An interesting topic to look into (if you want to manually implement this kind of things) is type erasure
Foo<int> and Foo<double> are 2 different classes despite they share the name Foo, so you can't just put them to vector as is. But you can use boost::variant and store a vector of variants.
Related
I have a class
class A{
vector<B> arr;
};
where
template <class T>
class B{
T member;
};
How want to be able to do something like
A container;
B<int> b1;
B<char> b2;
...
container.arr.push_back(b1);
container.arr.push_back(b2);
I tried adding template before class A, but i do not want to have to specify the template by class A, because then I wont be able to push objects of different types. How am I supposed to deal with this?
As mentioned in some comments, storing variables of distinct types into a std::vector might not be a good idea, and in fact, if you need to do so, chances are these types have something in common such that you can find more suitable approaches for achieving your goals, such as creating a std::vector<std::unique_ptr<Base_Type>>.
However, what you tried to do is still possible in a slightly different way using std::any.
template <typename T>
struct B
{
T member;
};
// ...
std::vector<std::any> v;
B<int> bi {123};
B<char> bc {'#'};
B<std::string> bs {"I am a string"};
v.push_back(bi);
v.push_back(bc);
v.push_back(bs);
// Go through vector. You will have to check for the type and
// cast it appropriately before doing anything useful.
for (auto x : v) {
if (x.type() == typeid(B<int>))
std::cout << std::any_cast<B<int>>(x).member << std::endl;
else if (/* ... */)
// ...
}
// ...
Another option could be a std::vector of std::variants, as explained by Vittorio.
A template is a compile-time code generation construct. In your code examples, B is not a type, but a template.
Templates can be instantiated at compile-time to generate a type (e.g. B<int>).
std::vector<T> is a container template class parametrized on T - it can only store objects of type T.
If you want to store objects of different types in the same container, you can:
Use std::tuple<Ts...> if you know the object sequence at compile-time;
Use something like std::vector<std::variant<A, B>> if you don't know whether an object is A or B until run-time.
This question already has answers here:
C++ templates that accept only certain types
(14 answers)
Closed 4 years ago.
I have several small classes that are all peers of each other declared and defined in the same file. A lot of these classes share information. Currently, the type of the shared information is hard-coded for initial development and testing purposes, but I want to templatize (verb form?) the classes. However, if I write the template construct before each class, that creates the possibility that a user could create instances of each class with different type arguments, which will most likely lead to errors in the data or code. Is there a way to force all class instances to be created with the same type?
The only way I can think of doing this is to create an additional init or spawner class with members functions like createInstanceOfA(), createInstanceOfB(), etc., where the user would have to first create an instance of the spawner class with the desired type, then use its member functions to create instances of the other classes. Of course, this would mean that my spawner class would have to stay in sync with whatever utility classes I have (which shouldn't be a problem). However, is there a better way of doing this?
EDIT: As an example, my "ugly solution" (a simple case):
template <typename T>
struct A {
void manipulate( T arg );
};
template <typename T>
struct B {
void manipulate( T arg );
};
template <typename T>
struct C {
void manipulate( T arg );
};
template <typename T>
struct Spawner {
A<T> createInstanceOfA( void );
B<T> createInstanceOfB( void );
C<T> createInstanceOfC( void );
};
int main() {
// don't allow
A<int> a;
B<float> b;
C<double> c;
// allow
Spawner<int> s;
A<int> s.createInstanceOfA(); // not sure if syntax is correct
B<int> s.createInstanceOfB();
C<int> s.createInstanceOfC();
return 0;
}
What you're asking for doesn't make sense. foo<int> is a different type than foo<float> - you shouldn't be running into issues with the wrong types.
What it seems you want is for to require 3 classes to be instantiated of the same class at the same time. What you want isn't 3 separate classes, but 1 single one: (example is composed of several classes built into one)
template <typename T>
struct col {
struct t1 {
T data;
} a;
struct t2 {
T data;
} b;
};
col<int> foo;
foo.a.data = 5;
foo.b.data = 7;
void process_stuff(col<int>::t1 a) {
// ...
}
process_stuff(foo.a);
What you're trying to achieve is called concepts by it's technical name, and there is a Boost library, ConceptCheck, that you can use to implement this.
You could also use std::enable_if and SFINAE (Substition Failure Is Not An Error).
But templates are meant to make code generic, and it doesn't really sound like that is what you want. I would reconsider your design.
You can specialize the templates.
So if you do:
template<typename T> class A;
template<>
class A<int>
{...};
template<>
class A<double>
{...};
Then if you or someone else tries to create a
A<std::string> a;
there will be a compile error because that type is not specialized.
But perhaps this is not what you want?
Edit:
I somewhat misunderstood the question. Perhaps you can solve this issue by controlling the creation of the classes? Like through a factory? If you only allow creation of the classes through the factory then you should be able to enforce the same type on multiple templates. Perhaps this is just pushing the problem to the factory class...
Good luck!
How do I declare a templated type that refers to itself?
template <class T = Animal> class Animal
{
public:
T getChild ();
}
With this, I get a compiler error concerning a missing type specifier. I tried to forward-declare Animal, without success.
I am trying to impose a type constraint. A Lion can only have a Lion as a child, a Bear has a Bear, and so on.
EDIT
I'll post part of the actual class. It is a template for classes that can appear in a linked list:
template <class T = Linked<T> > class Linked
{
private:
T* m_prev;
T* m_next;
}
I want to enforce that the class can only point to object of the same class (or a subclass).
In this case, you need to specify some type parameter to Animal in your typename definition, or else it would be an "infinite recursion" in the type construction:
template<class T> class Animal;//you'll need this forward declaration
template <class T = Animal<int> > class Animal //int is just an example
{
public:
T getPrey ();
}
The OP has been answered but I want to chime in because the immediate cause of the problem is not recursion, as others claim. The simplest reason this wouldn't work is that class templates are not types. They are templates. Similarly, function templates are not functions either. So all of this is nonsensical:
template<typename T> int function_template(int);
typedef int function_type(int);
void eats_a_function(function_type&); // needs a reference to function
std::vector< std::vector > vec0; // std::vector is not a type
std::vector< std::list > vec1; // std::list is not a type
eats_a_function(function_template); // function_template is not a function
Notice that in the vec1 case, std::list is not related to std::vector. The template is fully defined (assuming header inclusion) at the point of instantiation. It still won't work.
Instead, the following works:
std::vector< std::vector<int> > vec2; // std::vector<int> is a type
std::vector< std::list<double> > vec3; // std::list<double> is a type
eats_a_function(function_template<long>); // function_template<long> is a function
Notice that in the vec2 case, it's fine to pass an instantiation of the template itself.
For the record, a toy solution to the toy problem on writing a template that refers to itself, using the proverbial layer of indirection:
// expects a template that expects a type
template<template<class> class T> struct indirection {};
// forward decl. for defaulting the parameter
template<typename T> struct recursive;
// template that expects a type
template<typename T = indirection<recursive> > struct recursive {};
Not terribly powerful given the few things that are possible with a template (the T parameter inside indirection). It's of course possible to write a rebind-style metafunction that returns an instantiation of T.
The usual way to do something like a linked list is:
template <class T> class Linked
{
private:
Linked<T>* m_prev;
Linked<T>* m_next;
}
Does this work for you, and if not, what are you trying to accomplish that can't be done this way?
You can't create a template class wherein the template type is the class itself. This is a logical recursion that your compiler can't mitigate. Since templates require the compiler to construct the object when a specific typing is encountered ( say Animal ), you have to have a complete definition of the template type. Otherwise, the compiler will recursively try to construct objects. Since your object self-references, this will be an non-terminating recursion.
This use-case seems much more appropriate for inheritance than for templates.
Doing this will let you compile:
template class Animal
{
public:
T getPrey ();
}
Or, if you really wan't a default argument:
template <class T=Cheetah> class Animal
{
public:
T getPrey ();
}
Yet, cheetah must not use itself or Animal as one of its potential template types.
In this piece I'm trying to declare in Class B a list that can hold objects of Class A of any type, such as A<int>, A<double>, A<float>. I intend to add A objects to the list during runtime:
#include <list>
template <class T> class A {};
class B {
template<class T> std::list<A<T>*> objects;
};
It seems like making a list like this should work but compiling it gives an error:
Line 6: error: data member 'objects' cannot be a member template
compilation terminated due to -Wfatal-errors.
Can somebody explain why this doesn't work and how I can fix it?
That's just not how C++ works. If you want to group different objects together, they need to have at least some relation. Being instantiations of the same class template doesn't imply that they are related, they're completely distinct types. If you want a list of A<T>*s, better make a list of base-class pointers and forward operations through virtual functions:
class A_base{
public:
virtual void foo() = 0;
virtual ~A_base() { }
};
template<class T>
class A : public A_base{
public:
void foo(){
// ...
}
};
class B{
std::list<A_base*> objects;
};
Member variables aren't allowed to be templates. Only member functions can be templates. You'll have to templatize the enclosing class B instead:
template <class T>
class B {
std::list<A<T>*> objects;
};
Unfortunately you cannot have template variables. Only option to declare a member data is to make the class template:
template<class T>
class B {
std::list<A<T>*> objects;
};
Depending on what you're doing, type erasure might be an option. On the Tension Between Object-Oriented and Generic Programming in C++ is my favorite write-up on the subject.
In a nutshell, you convert the static dispatch enabled by the templates into dynamic dispatch through a custom inheritance tree you setup on the fly. Instead of storing A<T>, you create a new type that has the common interface you desire, and using some template/inhertiance voodoo this new type stores an A<T> without actually exposing the T. So A<int> and A<double> and A<A<std::list<A<int> > > > and some_type_that_looks_like_A_but_really_isnt all reduce down to a single type.
But you have to have a common interface, independant of that parameter. If you can't, things get more difficult.
Boost.Any is a good example, as is std::shared_ptr [which uses type erasure to remember how to delete the pointer passed to it even in the face of non-polymorphic inheritance].
Make B class template just like you've made A a class template:
template<class T>
class B {
std::list<A<T>*> objects;
};
Question as I have not done much with vectors and templates.
If I have a class foo that is templated class and I want to create a vector of foo pointers regardless of foo type, what would the syntax look like?
There is no direct way to do this. Different instantiations of the same template are treated as distinct classes with no relation to one another.
If you want to treat them uniformly, one option is to create a base class that the template foo class then inherits from. For example:
class foo_base {
/* ... */
};
template <typename T> class foo: public foo_base {
/* ... */
};
Now, you can create a vector<foo_base*> to store pointers to foo_base objects, which are in turn all specializations of foo.
You wouldn't. Every instantiation of the class template foo (e.g. foo<int>, foo<char> etc) is a distinct type. foo itself is not a type.
You'd need some base class for them and store pointers, making use of polymorphism.
Not possible. When you use a class template anywhere, you need it instantiated on a type.
One possibility to circumvent that, is to provide a polymorphic interface base class for foo and have a vector of those pointers.
class IFoo{
virtual void bar() = 0;
virtual int baz() = 0;
};
template<class T>
class Foo : IFoo{
// concrete implementations for bar and baz
};
// somewhere in your code:
std::vector<IFoo*> vecFoo.
vecFoo.push_back(new Foo<int>);
The obvious problem with that is, that you can't need to know each possible return value for your bar and baz functions.
You cannot have a vector of foo<?>. The compiler when it creates the vector needs to know precisely what type it stores because every instantiation of foo<?> could potentially have a different size and entirely different members (because you can provide explicit and partial specializations).
You need to give the foo<T> a common base class and then put baseclass pointers into the vector (preferably using shared_ptr or something similar).
As an alternative there are ready classes that hide this from you and act like a container of references. Among them is boost::ptr_vector, which however also need a common type to store.
class common_base {
/* functions to access the non-templates parts of a foo<T> .. */
};
template<typename T> class foo : public common_base { };
If you are trying to emulate concept: "vector of pointers to Foo" without specifying T, you are trying to emulate template typedefs that are not supported by current standard.
The workaround is
template <class T>
my_vector
{
typedef std::vector<Foo<T>*> type;
}
If, instead, you can afford polymorphic interface for your Foo guy, I would do that as it was already suggested.