I would like to store pointers to a Base class in a vector, but then use them as function arguments where they act as a specific class, see here:
#include <iostream>
#include <vector>
class Base {};
template<class T>
class Derived : public Base {};
void Foo(Derived<int>* d) {
std::cerr << "Processing int" << std::endl;
}
void Foo(Derived<double>* d) {
std::cerr << "Processing double" << std::endl;
}
int main() {
std::vector<Base*> vec;
vec.push_back(new Derived<int>());
vec.push_back(new Derived<double>());
Foo(vec[0]);
Foo(vec[1]);
delete vec[0];
delete vec[1];
return 0;
}
This doesn't compile:
error: call of overloaded 'Foo(Base*&)' is ambiguous
Is it possible to make it work? I need to process the elements of the vector differently, according to their int, double, etc. types.
You'll need to use method polymorphism, as it's dynamic, rather than function overloading, which is static (compile-time). To overload on a templated type, you'll need to use template specialization.
Example:
#include <iostream>
#include <vector>
class Base {
public:
virtual void Foo() {
std::cerr << "Processing base" << std::endl;
}
};
template<class T>
class Derived : public Base {};
template <>
class Derived <int> : public Base {
public:
void Foo() {
std::cerr << "Processing int" << std::endl;
}
};
template <>
class Derived <double> : public Base {
public:
void Foo() {
std::cerr << "Processing double" << std::endl;
}
};
int main() {
std::vector<Base*> vec;
vec.push_back(new Derived<int>());
vec.push_back(new Derived<double>());
vec[0]->Foo();
vec[1]->Foo();
delete vec[0];
delete vec[1];
return 0;
}
When you typecast from Derived<int>* to Base*, you lose the information about what derived class you have, unless you use RTTI (RunTime Type Identification).
If you have enabled RTTI, then you can try to typecast a Base* forward to a derived type pointer by using dynamic_cast<>():
void Foo(Base* base)
{
Derived<int>* derivedInt = dynamic_cast<Derived<int>*>(base);
if(derivedInt)
{
Foo(derivedInt);
return;
}
Derived<double>* derivedDouble = dynamic_cast<Derived<double>*>(base);
if(derivedDouble)
{
Foo(derivedDouble);
return;
}
// Handle other cases here.
}
dynamic_cast returns NULL if the pointer doesn't point to the correct type.
Alternatively, if you don't want to use RTTI and dynamic_cast, you have to maintain some means for determining which subclass is stored in your vector (usually an enum value stored along with the pointer, maybe in a pair, or with a method in Base that returns a similar enum) and use reinterpret_cast<>() to typecast the pointer.
Of course it is ambiguous, how can the compiler figure out which specific subclass of Base is held in vec[0].
You can resolve the ambiguity by an explicit cast:
Foo( (Derived<int>*) vec[0] )
or, better, consider using dynamic method resolution:
class Base {
virtual void Foo() = 0;
};
template <class T>
class Derived: Base {
void Foo() { /* .. doFoo<T>(...) .. */ }
};
template<class T> void doFoo() { /* .. general case .. */ }
template<> void doFoo<int>() { /* .. int case .. */}
template<> void doFoo<double>() { /* .. double case .. */}
and in your code just call
vec[0]->Foo()
You can do it like this:
#include <iostream>
#include <vector>
class Base {
public:
virtual void DoFoo() = 0;
};
template<class T>
class Derived : public Base {
public:
virtual void DoFoo() {
Foo(this);
}
};
void Foo(Derived<int>* d) {
std::cerr << "Processing int" << std::endl;
}
void Foo(Derived<double>* d) {
std::cerr << "Processing double" << std::endl;
}
int main()
{
std::vector<Base*> vec;
vec.push_back(new Derived<int>());
vec.push_back(new Derived<double>());
vec[0]->DoFoo();
vec[1]->DoFoo();
delete vec[0];
delete vec[1];
return 0;
}
Related
I'm currently coding a program in c++ using a template class:
template<typename TYPE>
class TemplateClass {
private:
TYPE t;
};
I have another class which acts as manager of my TemplateClass which should store multiple instances of this class in a vector. Different instances should have different types e.g. int, std::string, etc.. Speaking in Java ways the solution would be to just use something like in the example below but it seems like this is not possible in C++.
class ManagerClass {
private:
// Here seems to be the problem.
std::vector<TemplateClass<?>> templates;
}
Is it possible to do something like that?
Thank you for all answers
If you know all the types that will be stored in the std::vector at compile time I'd use an std::variant in such a case.
// This is used for the visitor pattern.
template<class... Ts> struct overload : Ts... { using Ts::operator()...; };
// The below line not needed in C++20...
template<class... Ts> overload(Ts...) -> overload<Ts...>;
template<typename T>
struct MyClass { T value; };
using types = std::variant<
MyClass<std::string>,
MyClass<int>,
MyClass<double>>;
int main()
{
std::vector<types> stuff{};
stuff.push_back(MyClass<std::string>{});
stuff.push_back(MyClass<int>{});
stuff.push_back(MyClass<double>{});
for(const auto& v : stuff)
{
if (std::holds_alternative<MyClass<std::string>>(v))
{
std::cout << "Im a string\n";
}
else if (auto* p{std::get_if<MyClass<int>>(&v)})
{
std::cout << "Im an int\n";
}
else
{
auto t = std::get<MyClass<double>>(v);
std::cout << "Im a double\n";
}
// Or you can use the visitor pattern.
std::visit(overload{
[](const MyClass<std::string>& ) { std::cout << "I'm a string\n"; },
[](const MyClass<int>& ) { std::cout << "I'm a int\n"; },
[](const MyClass<double>& ) { std::cout << "I'm a double\n"; },
}, v);
}
}
If you can use C++17, you can use std::any or std::variant.
class ManagerClass {
private:
using variant_type = std::variant<
TemplateClass<std::string>,
TemplateClass<int>,
TemplateClass<double> >;
std::vector<variant_type> templates;
};
This is one way of doing it via runtime polymorphishm, which is achieved by function overriding. Function overriding occurs when a derived class has a definition for one of the member functions of the base class. That base function is said to be overridden.
#include <iostream>
#include <vector>
using namespace std;
// Base class declaration
class Base {
public:
virtual void print()
{
cout << "Base" << endl;
}
virtual ~Base(){}
};
// Derived Class 1
class Derived1 : public Base {
public:
void print()
{
cout << "Derived1" << endl;
}
};
// Derived class 2
class Derived2 : public Base {
public:
void print()
{
cout << "Derived2" << endl;
}
};
int main()
{
Base* d1 = new Derived1();
Base* d2 = new Derived2();
vector<Base*> myVec;
myVec.push_back(d1);
myVec.push_back(d2);
for (auto i : myVec) {
i->print();
}
delete d1;
delete d2;
return 0;
}
How would I provide a template specialisation for the Derived class in this simple case, assuming the Base cannot be changed - in my real code, Base is a library that I cannot change.
#include <iostream>
#include <memory>
class Base
{
public:
virtual void foo() { std::cout << "In base\n"; }
};
class Derived: public Base
{
public:
virtual void foo() { std::cout << "In derived\n"; }
};
template<typename T>
void wibble(T &&baz)
{
std::cout << "In wibble for default\n";
baz->foo();
}
// How do I provide a specialisation here?
//template<typename what_goes_here>
//void wibble(what_goes_here &&baz)
//{
// std::cout << "In wibble for derived\n";
// baz->foo();
//}
int main()
{
std::shared_ptr<Base> bar = std::make_shared<Derived>();
bar->foo();
wibble(bar);
return 0;
}
I want to be able to use a separate template when the actual type contained within the shared pointer is Derived.
The proper tool here is to use dynamic_cast<Derived*>. This is runtime information and as such must be queried at runtime.
Something like
void wibble(std::shared_ptr<Base*> baz){
if(auto derived =dynamic_cast<Derived*>(baz.get())){
// user derived pointer.
}
}
should allow you achieve something similar to what you want.
If your keyboard is missing *, you can also (thanks Eljay)
if(auto derived = std::dynamic_pointer_cast<Derived>(baz)){
// use derived
}
This is just one more case where templates and polymorphism doesn't really play nice with each other, mainly because of the compile time/runtime dichotomy.
My code looks something like this:
class A {
...
};
template<typename T>
class B: public A {
...
};
A* pointerA = new B<X>(...);
But now I want to cast another pointerB to pointerA:
B<...>* pointerB = (B<...>*)pointerA;
How do I know what to insert into <...> or how I should do this correctly?
You can try the cast with dynamic_cast. If you dynamic_cast to a pointer type and it fails, you get a null pointer back. If you dynamic_cast to a reference type and it fails, you'll get an exception.
Example:
#include <iostream>
class A {
public:
virtual ~A() = default;
};
template<typename T>
class B : public A {};
int main() {
A* pointerA = new B<int>;
auto pointerB = dynamic_cast<B<double>*>(pointerA);
if(pointerB) {
std::cout << "cast succeeded\n";
} else {
std::cout << "cast failed\n"; // will print "cast failed"
}
delete pointerA;
}
If you have many of them stored in a vector you need to test all possible types to be able to cast back to the original type.
Example:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class A {
public:
virtual ~A() = default;
};
template<typename T>
class B : public A {};
int main() {
std::vector<std::unique_ptr<A>> vec;
vec.emplace_back(new B<int>);
vec.emplace_back(new B<std::string>);
for(auto& ptr : vec) {
if(auto b = dynamic_cast<B<int>*>(ptr.get())) {
std::cout << "B<int>\n";
} else if(auto b = dynamic_cast<B<std::string>*>(ptr.get())) {
std::cout << "B<std::string>\n";
} else {
std::cout << "Oh, unknown derived type stored ...\n";
}
}
}
It's preferable to add virtual methods to the base class and implement them in the derived classes to not have to do this cast at all though.
Suppose I have a base class as below:
template <typename T>
class Base {
// implementation
void do_something() { /* ... */ } ;
};
then, I create a Derived class as below, and override the do_something() method:
template <typename T>
class Derived : public Base<T> {
// implementation
void do_something() { /* ... */ } ;
};
I know virtualization does not work in class templates, and I am just hiding the implementation of the methods. but I do want to store a bunch of derived classes and base classes into a vector, (I do not want to use type erasure, or polymorphism),
my question is, given that static_cast of Derived class to base class gives me the do_something of based class, Is there any way that I can store them as base classes while each has their implementation of do_something() class ?
but I do want to store a bunch of derived classes and base classes into a vector, (I do not want to use type erasure, or polymorphism),
This is already just not possible in C++. In C++, a vector can only contain objects of the same static type. The only way a vector can contain different types of objects is if their static type is still the same, but they have different dynamic types, but this is type erasure/polymorphism which you said you don't want to use.
I think maybe you need to rethink your requirements, because your question in essence reads: I want to do something, but I don't want to use technique X which is explicitly defined as the only way to do that something in C++!
I did this and it seems to work fine:
#include <iostream>
template <typename T>
struct Base {
virtual void do_something() { std::cout << "Base::do_something()\n"; }
};
template <typename T>
struct Derived : public Base<T> {
virtual void do_something() { std::cout << "Derived::do_something()\n"; }
};
int main() {
Base<int> b;
Derived<int> d;
Base<int> *p;
p = &b;
p->do_something();
p = &d;
p->do_something();
return 0;
}
Output:
Base::do_something()
Derived::do_something()
A little variation of the melpomene's answer (adding a no-template base struct, BaseOfBase, for the Base<T> structs) permit the use of a common vector of base of derived classe of different T types.
A working example
#include <vector>
#include <iostream>
struct BaseOfBase
{ virtual void do_something () = 0; };
template <typename T>
struct Base : public BaseOfBase
{
T val;
void do_something ()
{ std::cout << "Base::do_something() [" << val << "]\n"; };
};
template <typename T>
struct Derived : public Base<T>
{ void do_something()
{ std::cout << "Derived::do_something() [" << this->val << "]\n"; } };
int main ()
{
std::vector<BaseOfBase*> vpbb;
Base<int> bi;
Derived<int> di;
Base<std::string> bs;
Derived<std::string> ds;
bi.val = 1;
di.val = 2;
bs.val = "foo";
ds.val = "bar";
vpbb.push_back(&bi);
vpbb.push_back(&di);
vpbb.push_back(&bs);
vpbb.push_back(&ds);
for ( auto const & pbb : vpbb )
pbb->do_something();
}
When we say virtualization doesn't work in template classes, we don't mean that you can't do virtual functions in a template class, nor does it mean that you cannot override a member function with a specialized version of it.
#melpomene showed an example of overriding in general, and I will show here with specialization:
#include <iostream>
template <typename T>
class Base {
public:
virtual T do_something(T in) { std::cout << "Base::do_something()\n"; return in; }
};
class Derived : public Base<int> {
public:
virtual int do_something(int in) { std::cout << "Derived::do_something()\n"; return in - 1; }
};
void main()
{
Base<int> b;
Derived d;
Base<int> *p = &b;
auto r1 = p->do_something(10);
std::cout << r1 <<std::endl;
p = &d;
auto r2 = p->do_something(10);
std::cout << r2 << std::endl;
}
Which will output
Base::do_something()
10
Derived::do_something()
9
Showing that it perfectly works as expected.
What we do mean when saying that
virtualization does not work in class templates
Basically means that you can't use as a template the derived class when the base is expected.
Consider the above classes Base<T> and Derived, then if we have the following code:
#include <memory>
template <typename T>
void Test(std::unique_ptr<Base<T>> in){ std::cout << "This will not work with derived"; }
void main()
{
Base<int> b;
Derived d;
auto ptr = std::unique_ptr<Derived>(&d);
Test(ptr); // <-- Will fail to compile as an invalid argument
}
it will fail because std::unique_ptr<Derived> does not inherit from std::unique_ptr<Base<T>> although Derived itself inherits from Base<T>.
We would like to specialize member functions of a base class. However, it does not compile. Does anybody know of any alternative that does compile?
Here is an example
struct Base
{
template<typename T>
void Foo()
{
throw "Foo() is not defined for this type";
}
};
struct Derived : public Base
{
template<>
void Foo<int>() { cout << "Foo<int>()" << endl; } // compile error (cannot specialize members from a base class)
template<>
void Foo<double>() { cout << "Foo<double>()" << endl; } // compile error (cannot specialize members from a base class)
};
Eventually, we solved it using overloading.
Here is how the base class looks like
struct Base
{
template<typename T>
class OfType {}
template<typename T>
void Foo(OfType<T>) { static_assert(false, "Foo is not implemented for this type. Please look in the compiler error for more details."); }
};
struct Derived : public Base
{
using Base::Foo;
void Foo(OfType<int>) { // here comes logic for ints }
void Foo(OfType<double>) { // here comes logic for doubles }
};
Here is an example of client code that uses Foo()
template<typename S>
class ClassThatUsesFoo
{
private: S s;
template<typename T>
void Bar(T item)
{
s.Foo(Base::OfType<T>()); // this is the code that uses Foo
DoSomeStuffWithItem(item);
}
};
void main()
{
ClassThatUsesFoo<Derived> baz;
baz.Bar(12); // this will internally use Foo for ints
baz.Bar(12.0); // this will use Foo for doubles
baz.Bar("hello world"); // this will give a verbose compile error
}
This will compile, except for the call to Foo<char>():
#include <iostream>
#include <string>
using namespace std;
struct Base
{
template<typename T>
void Foo()
{
throw "Foo() is not defined for this type";
}
};
struct Derived : public Base
{
template<typename T> void Foo();
};
template<>
void Derived::Foo<int>() { cout << "Foo<int>()" << endl; }
template<>
void Derived::Foo<double>() { cout << "Foo<double>()" << endl; }
int main()
{
Derived derived;
// this is client code
derived.Foo<int>();
derived.Foo<double>();
derived.Foo<char>(); // this throws
}
If you want the call to Foo<char>() -- or any type not specifically specialized by you -- then this works. If you want a non-specialized implementation that works for all types, then you need to add a non-specialized implementation of Foo() as well:
template<typename T>
void Derived::Foo() { cout << "generic" << endl; }
In response to the discussion with Alex (see comments of the answer of John Dibling), this is what I meant (SSCCE):
#include <iostream>
using namespace std;
struct Base
{
template<typename T>
void Foo()
{
//static_assert(false, "Foo() is not defined for this type");
throw "Foo() is not defined for this type";
}
};
// you can add as many specializations in Base as you like
template <>
void Base::Foo<char>() { cout << "Base::Foo<char>()" << endl; }
struct Derived : public Base
{
// just provide a default implementation of Derived::Foo
// that redirects the call to the hidden Base::Foo
template < typename T >
void Foo()
{ Base::Foo<T>(); }
};
// the specializations for Derived
template<>
void Derived::Foo<int>() { cout << "Foo<int>()" << endl; }
template<>
void Derived::Foo<double>() { cout << "Foo<double>()" << endl; }
struct Derived_wo_specialization : public Base
{
/* nothing */
};
int main()
{
Derived d;
d.Foo<char>();
d.Foo<double>();
Derived_wo_specialization dws;
dws.Foo<char>();
dws.Foo<double>();
}