0

I have to hold different type of datas in one array for my project. I've created a template class for generating objects.

template<class Queue>
class Template  {

public:
    Queue value;
    Template(Queue input) {
        value = input;
    }

};

But I can't hold them in one array without using abstract class. I've created a void pointer array for this. And I used it liked that;

void *array[21];
array[index] = new Template<int>(number);
array[index] = new Template<string>(text);

Is there any possible solution without abstract classes? I mean, can i hold this template objects in template class' array?

4
  • 1
    You misunderstood template parameters perhaps. How does int represent a Queue actually? Commented Nov 3, 2015 at 19:48
  • No, i'll use different type of datas with queue implementation. I've named it because of that. Commented Nov 3, 2015 at 20:05
  • You'll need some intermediate template class like template <class T> class Queue; probably, where T instantiated as int, std::string respectively. Commented Nov 3, 2015 at 20:32
  • Do you mind if I ask why you removed that code snippet? You are removing necessary context for other readers to understand people's answers. Commented Nov 3, 2015 at 20:46

3 Answers 3

5

Create a hierarchy and take advantage of dynamic binding:

class Base  {
public:
  virtual ~Base() {};
  // ...
};

template<class Queue>
class Template : public Base {
    Queue value;
public:
    Template(Queue const &input) :value(input) {}
    // ...
};

And use it as:

Base *array[21];
array[index] = new Template<int>(number);
array[index + 1] = new Template<string>(text);

Furthermore, instead of using raw array and raw pointers use STL facilities like std::array smart pointers (e.g., std::shared_ptr<Base> or std::unique_ptr<Base>):

std::array<std::unique_ptr<Base>, 21> arr;
arr[index].reset(new Template<int>(number));
arr[index + 1].reset(new Template<string>(text));

Also prefer to initialize member variables to the constructor's initializer list than to its body.

Sign up to request clarification or add additional context in comments.

4 Comments

Good answer. also note that, if he needed the value, he can have a virtual function in Base<> and override it in Template, taking advantage of co-variant return types!
@Moo-Juice Covariant return types are great, but wouldn't work for his example of int and std::string. The base class here allows to store these values in a uniform array, but I still have to wonder why he needs this. As is, he'd have to dynamic_cast every time he wants to get the data back out.
@NathanOliver, actually that's not true. You can override a virtual function and provide a different return-type provided it is co-variant. However as GuyGreer points out, it's not suitable in this instance.
It may be worth adding the virtual destructor to Base explicitly, in case the OP doesn't know that he needs that.
2

This is usually a sign that you should rethink your code structure at a higher level so that you don't need this at all.

Otherwise you have four choices that I see (five if you count the "don't do this" above):

  1. Use a uniform type that can hold all your types of data (for example a std::string and parse the numeric information out when needed). This functionality could be wrapped in a class that provides member functions to make this easier.

  2. Use a boost::variant, if you are new to C++ then I don't recommend tackling this sort of thing right away.

  3. Use a base class as explained by 101010. I would add that you may want an enum in the base class that tells you what type of data is stored

  4. Use a boost::any, this is even more difficult to use than a variant, even though it's easier to understand.

Without more information on what it is you are trying to achieve, we can't really provide any better guidance on how to proceed.

2 Comments

@jesta what will these commands do other than "put" things? Where are they putting them? How much do you need to know about what the types are that you are putting somewhere?
@jesta, from what you say, I would recommend solution 1.
1

I'd do it using variant types. You can roll out your own but prefer using boost::variant :

#include <boost/variant.hpp>
#include <iostream>
#include <string>

using namespace std;

template<class Queue>
class Template  
{
public:
    Queue value;
    Template() = default; 
    Template(Queue input) {
        value = input;
    }
};

template<typename T>
std::ostream& operator<<(std::ostream& os, Template<T> const& t)
{
    os << t.value; 
    return os; 
}

int main ()
{
    using v_t = boost::variant<Template<int>, Template<string>>; 

    v_t ar[2]; 

    ar[0] = Template<int>(1); 
    ar[1] = Template<string>("lmfao"); 

    for (auto&& elem : ar) cout << elem << endl;
}

Demo

Note that

  • v_t is the type of the variant
  • v_t can have more types among which you can choose to populate the array
  • the output operator was only overloaded for demonstration
  • you get extra functionality by boost::visitor

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.