Important: This question is getting quite long, if this is the first time you’re reading this I suggest you start near the bottom as the solution is there in a round about way, but the code is a bit smelly.
After reading a tutorial on templates, I was able to change my existing class to support generic types. However, many objects are already dependent on this, so I’m looking for a way of making a method generic rather than the entire class.
I’ve tried the following, but it looks like this behavior isn’t supported.
// foobar1.h // Don't want the entire class to be generic. //template<class T> class FooBar1 { public: template<class T> T Foo(); } // foobar2.h class FooBar2 : public FooBar1 { } // foobar1.cpp template<class T> T FooBar1::Foo() { return something; } // test.cpp FooBar1 fb1; FooBar2 fb2 = fb1.Foo<FooBar2>();
Is this supposed to not work, or is it a bug elsewhere that I’m getting confused with?
undefined reference to
FooBar2 Foo<FooBar2>()
To put this in to some sort of perspective as to what I want to achieve, here’s how I’d do it in C# …
public class FooBar1 { public T Foo<T>() where T : FooBar1 { return something; } } public class FooBar2 : FooBar1 { } FooBar1 fb1 = new FooBar1(); FooBar2 fb2 = fb1.Foo<FooBar2>();
Is there any way I can do something similar to that in C++?
Update 1:
Just corrected some minor syntax details (I meant to make Foo public, and return T, not FooBar2). Still getting compiler error… When I remove the template behavior the error goes away, the answer so far say what I’m doing is valid… but if it is then why am I getting the error still? Thanks for your answers!
Update 2:
Josh, here’s the actual source code (well, what I think is relevant, anwyay – let me know if you think I’ve skipped an important bit).
// ImageMatrix.h class ImageMatrix : public VImage { public: // ... various functions ... template<class T> T GetRotatedCopy(VDouble angle); } // ImageFilter.h class ImageFilter : public ImageMatrix { // ... various functions ... } // ImageMatrix.cpp template<class T> T ImageMatrix::GetRotatedCopy(VDouble angle) { // ... create a new instance of ImageMatrix and return it. } // ImageProcessor.cpp ImageFilter filter2 = filterPrototype.GetRotatedCopy<ImageFilter>(90);
And here’s the actual compiler error:
/home/nick/Projects/ViMRID/vimrid/Debug/libvimrid.so: undefined reference to `vimrid::imaging::processing::ImageFilter vimrid::imaging::ImageMatrix::GetRotatedCopy(double)’
Update 3:
By the way, everything but the implementation line is located in a library; so it’s being called from a separate binary… Does this matter? Correction; its all in the same library. All blocks are different files though.
Update 4:
When I comment out the implementation line (ImageFilter filter2 = filterPrototype…) it builds fine, so it seems to be this line thats causing it…
Update 5 (Solved?):
Still having problems… Could this be a problem with the namespaces? Scratch that, OK, I’ve grasped the concept of templates now! 🙂 The template definition must be in the header along with the declaration (right?) – so now that I’ve moved the declaration in to ImageMatrix.h, everything compiles. However, I’ve had to use dynamic_cast to get it working; is this right? If I’m way off please correct me!
// This is in the header file! // Help!!! This looks really really smelly... template<class T> T ImageMatrix::GetRotatedCopy(VDouble angle) { ImageMatrix image = _getRotatedCopy(angle); ImageMatrix *imagePtr = ℑ return *dynamic_cast<T*>(imagePtr); }
Update 6:
Refering to update 5, when I don’t use dynamic_cast…
template<class T> T ImageMatrix::GetRotatedCopy(VDouble angle) { ImageMatrix image = _getRotatedCopy(angle); ImageMatrix *imagePtr = ℑ //return *dynamic_cast<T*>(imagePtr); return *imagePtr; }
… I get this error …
../src/imaging/processing/../ImageMatrix.h: In member function ‘T vimrid::imaging::ImageMatrix::GetRotatedCopy(vimrid::VDouble) [with T = vimrid::imaging::processing::ImageFilter]’: ../src/imaging/processing/ImageProcessor.cpp:32: instantiated from here ../src/imaging/processing/../ImageMatrix.h:45: error: conversion from ‘vimrid::imaging::ImageMatrix’ to non-scalar type ‘vimrid::imaging::processing::ImageFilter’ requested make: *** [src/imaging/processing/ImageProcessor.o] Error 1
Update 7:
Also, if I don’t use all of that smelly code in update 6…
class ImageMatrix : public VImage { public: template<class T> T GetRotatedCopy(VDouble angle); private: ImageMatrix _getRotatedCopy(VDouble angle); }; template<class T> T ImageMatrix::GetRotatedCopy(VDouble angle) { return _getRotatedCopy(angle); }
… I get the same error as in update 6.
Yup, you were pretty close, try this:
The issue you were having is that you were specifying the return type of
FooBar1::Foo()asFooBar2, you should have it as justT.If you want to do specific things for FooBar2, you can specialize the on FooBar2:
Edit: It sounds like you are having issues with the compiler not finding the definition for your templated GetRotatedCopy. Templates in C++ are rather finicky, and the usual practice is to put the entire template implementation in a header file. You might try this:
Edit: I can’t find the gcc documentation, but here’s microsoft’s documentation on explicit template instantiation and libraries, it gives a bit of an idea of what’s happening. You’ll likely want to either include the implementation in the header as I suggested earlier, or call GetRotatedCopy in the library, or else instantiate it explicitly in the library. See veefu’s answer below for the syntax.
The reason this works differently from C#, is that templates in C++, unlike C#, actually create a whole new class/function for each different combination of template parameters. e.g.
vector<int>is a completely different class (with a different set of compiled methods) thanvector<string>. See Kevin’s answer for a better explanation.As to the error going away when you don’t use the template, that doesn’t actually tell you much, since until you actually instantiate a template, it’s not going to
RE Update 5,6,7
Your dynamic_cast isn’t going to work, you can only use it if the pointer is actually pointing to an instance of the class you are casting to. (It works similar to the
asoperator in C#).I suspect now, that what you are wanting is the CRTP. You start out with an instance of ImageFilter, and want to use a base class method on it, and get back a new copy of ImageFilter. Try something along these lines:
Otherwise, if you really do want to start out with an ImageMatrix and transform it into an ImageFilter, you’ll have to add a constructor on ImageFilter that takes an ImageMatrix.