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.
Related
I am wondering if it is possible to convert a vector of derived class values to a vector of base class values. Specifically I want to be able to pass a vector of base class objects to a function whose formal parameters takes a vector of base class. It does not appear to be possible directly as the following code example produces an error (using g++):
#include <vector>
class A {
};
class B : public A {
};
void function(std::vector<A> objs) {
}
int main(int argc, char **argv) {
std::vector<B> objs_b;
objs_b.push_back(B());
function(objs_b);
}
test.cc:16: error: conversion from ‘std::vector<B, std::allocator<B> >’ to non-scalar type ‘std::vector<A, std::allocator<A> >’ requested
I would like to be able to be able to call function without having to define a new vector with elements of type A, inserting my elements of type B or changing to a vector of pointers.
No, it is not. vector<B> is not derived from vector<A>, regardless of the fact that B is derived from A. You will have to change your function somehow.
A more idiomatically C++ approach might be to template it and have it take a pair of iterators - this is why the various standard library functions (<algorithm> etc) work that way, because it separates the implementation of the algorithm from the kind of thing it's operating on.
Sometimes there are ways around this. Take a loot at Boost and some of the template meta-programming packages they have or use. Specifically I'd look at is_base_of for these kinds of purposes combined with partial template specialization.
For instance, if you wish to do some template magic trickery:
template<typename T, bool Allowed>
struct TemplateVectorCode;
You make a forward declaration of a templated class. Then you make a specialization with a "true" boolean value:
template<typename T> struct TemplateVectorCode<T, true>{
void operator(const std::vector<T>& myVector) const{ //definition in here }
};
Finally you use that with is_base_of to only instantiate instances of your template functor in the following manner:
template<typename T, typename Base>
struct MyFunction : TemplateVectorCode<T, boost::is_base_of<Base,T>::value>{
};
The important point to note is that since we have not defined a TemplateVectorCode<T, false> the compiler will throw a compile error if you attempt to use your functor with a class type T that does not derive from the type V. That means you can work with the same code in one place without having to write multiple versions of it.
(if I screwed up the partial template specialization, someone please edit it. I need to go to bed.)
Here is how to make your function accept standard vectors of any child classes of a given type (in this case, class A) using templates in C++14.
class A {};
class B : public A {};
template<typename T, std::enable_if_t<std::is_base_of_v<A, T>, bool> = true>
void function(const std::vector<T>& vec) {
// implemenation
}
std::vector<A> vec_a;
std::vector<B> vec_b;
function(vec_a); // ok
function(vec_b); // ok
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.
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!
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;
};
I am wondering if it is possible to convert a vector of derived class values to a vector of base class values. Specifically I want to be able to pass a vector of base class objects to a function whose formal parameters takes a vector of base class. It does not appear to be possible directly as the following code example produces an error (using g++):
#include <vector>
class A {
};
class B : public A {
};
void function(std::vector<A> objs) {
}
int main(int argc, char **argv) {
std::vector<B> objs_b;
objs_b.push_back(B());
function(objs_b);
}
test.cc:16: error: conversion from ‘std::vector<B, std::allocator<B> >’ to non-scalar type ‘std::vector<A, std::allocator<A> >’ requested
I would like to be able to be able to call function without having to define a new vector with elements of type A, inserting my elements of type B or changing to a vector of pointers.
No, it is not. vector<B> is not derived from vector<A>, regardless of the fact that B is derived from A. You will have to change your function somehow.
A more idiomatically C++ approach might be to template it and have it take a pair of iterators - this is why the various standard library functions (<algorithm> etc) work that way, because it separates the implementation of the algorithm from the kind of thing it's operating on.
Sometimes there are ways around this. Take a loot at Boost and some of the template meta-programming packages they have or use. Specifically I'd look at is_base_of for these kinds of purposes combined with partial template specialization.
For instance, if you wish to do some template magic trickery:
template<typename T, bool Allowed>
struct TemplateVectorCode;
You make a forward declaration of a templated class. Then you make a specialization with a "true" boolean value:
template<typename T> struct TemplateVectorCode<T, true>{
void operator(const std::vector<T>& myVector) const{ //definition in here }
};
Finally you use that with is_base_of to only instantiate instances of your template functor in the following manner:
template<typename T, typename Base>
struct MyFunction : TemplateVectorCode<T, boost::is_base_of<Base,T>::value>{
};
The important point to note is that since we have not defined a TemplateVectorCode<T, false> the compiler will throw a compile error if you attempt to use your functor with a class type T that does not derive from the type V. That means you can work with the same code in one place without having to write multiple versions of it.
(if I screwed up the partial template specialization, someone please edit it. I need to go to bed.)
Here is how to make your function accept standard vectors of any child classes of a given type (in this case, class A) using templates in C++14.
class A {};
class B : public A {};
template<typename T, std::enable_if_t<std::is_base_of_v<A, T>, bool> = true>
void function(const std::vector<T>& vec) {
// implemenation
}
std::vector<A> vec_a;
std::vector<B> vec_b;
function(vec_a); // ok
function(vec_b); // ok