I'm trying to make a C++ Template
class that can store a template value. However, I need to create pointers to this class before the type of the template value is known. To do this, I created an abstract Base
class that the template class inherits from. I create pointers to Base
, and when they are allocated, I use basePtr = new Template<TYPE>
.
The problem here is in assigning a value to Template
. Every way I can think of to do so (I'd LIKE to use an overloaded assignment operator) requires a method with a template datatype as a formal parameter. Because Template
objects can only be accessed through Base
pointers, I have to make a virtual function in Base
. However, virtual methods cannot contain template datatypes, and the virtual method in Base
's signature must match that of Template
's method.
Here's an example of what I'm going for:
class Base {
public:
/* THIS IS ILLEGAL - can't have template virtual method
template <class V>
virtual void operator =(const V& newValue) = 0;
*/
};
template <class ValueType>
class Template : public Base {
private:
ValueType *value;
public:
void operator =(const ValueType& newValue) {
*value = newValue;
}
};
int main() {
Base *theObject; // Datatype for template not known
theObject = new Template<string>; // At this point, it will be known
// NOW, THIS DOESN'T WORK - no virtual assignment overload in Base
*theObject = "Hello, world!";
return 0;
}
I apologize if I'm going about this the entirely wrong way, and if my method is stupid--this is my first foray into true OOD. Is there a way around this problem that I'm not seeing? I know I could create a long list of pure virtual functions in Base
that overload the assignment operator with different input types, like so:
virtual void operator =(const string& newValue) = 0;
virtual void operator =(const int& newValue) = 0;
virtual void operator =(const long& newValue) = 0;
...
But, I want the user to be able to insert custom class objects (or, more likely, pointers to those objects) into Template::value
, and I there's no way that can be done with the above methodology.
One way of doing what you are trying to achieve is to use Boost.Any (a well-known header-only library). Below is a demonstration. The code is commented step-by-step, so you should be able to understand it, and here is a live example:
#include <stdexcept> // Standard header for exception classes
#include <string> // Standard header for std::string
#include <boost/any.hpp> // The Boost.Any library header
#include <iostream> // This program prints some output
class Base
{
public:
virtual void operator = (boost::any val) = 0;
// ^^^^^^^^^^
// This can hold (almost) "any" value
// You need a virtual destructor if you want to delete objects
// of subclasses of this class through a pointer to this class!
virtual ~Base() { }
};
template <class ValueType>
class Template : public Base
{
private:
ValueType value;
// ^^^^^^^^^
// I do not see why using a pointer in this case. Manual memory
// management just complicates things. However, if your really
// need to do it and your really know what you're doing, go ahead.
// Just remember to delete your pointer at destruction and not to
// dereference it before it points to an allocated object (in your
// original text, both of these things are NOT done correctly).
public:
virtual void operator = (boost::any val)
{
// Attempt a cast to detect if the value we are trying to
// assign to this object is of the appropriate type...
ValueType* pVal = boost::any_cast<ValueType>(&val);
if (pVal == nullptr)
{
// ...it is not! Throw an exception...
throw std::logic_error("Incompatible type");
}
// The value is OK: assign it...
value = *pVal;
}
};
int main()
{
Base *theObject;
theObject = new Template<std::string>;
try
{
// This assignment will succeed...
// Wrapping the string literal in a std::string object is necessary
// because boost::any cannot be initialized from an array (and in C++
// string literals are arrays of characters).
*theObject = std::string("Hello, world!");
// This assignment will fail!
*theObject = 1;
}
catch (std::logic_error const& e)
{
// Handle the exception...
std::cout << e.what();
}
delete theObject; // <=== DON'T FORGET THIS!
return 0;
}