Does C ++ support individual generic methods, not generic classes?

Important: This question becomes quite long, if you are reading this for the first time, I suggest you start from scratch, as the solution exists in a roundabout way, but the code is a little smelly.

After reading the template manual, I was able to modify an existing class to support generic types. However, many objects already depend on this, so I'm looking for a way to create a generic method, and not the entire class.

I tried the following, but it seems that this behavior is not 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 confused with?

undefined reference to FooBar2 Foo<FooBar2>()

To imagine this in some perspective regarding what I want to achieve, here is how I would do it in C # ...

 pubic 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 a way to do something like this in C ++?

Update 1:

Some minor syntax details have been fixed (I wanted to make Foo public and return T, not FooBar2). Still getting a compiler error ... When I remove the behavior of the template, the error disappears, the answer still says what I am doing is valid ... but if so, why am I still getting the error? Thank you for your responses!

Update 2:

Josh, this is real source code (well, what I find relevant, anwyay - let me know if you think I missed 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 is the real compiler error:

/home/nick/Projects/ViMRID/vimrid/Debug/libvimrid.so: undefined link to `vimrid :: imaging :: processing :: ImageFilter vimrid :: imaging :: ImageMatrix :: GetRotatedCopy (double) '

Update 3:

By the way, everything except the implementation string is in the library; therefore it is called from a separate binary file ... Does it matter? Correction; all this in the same library. All blocks are different files.

Update 4:

When I comment on the implementation line (ImageFilter filter2 = filterPrototype ...), it builds fine, so this line seems to call it ...

Update 5 (allowed?):

Still having problems ... Could this be a problem with namespaces? Scratch, that, OK, I understood the concept of templates now! :) The template definition should be in the header along with the ad (on the right?) - so now that I have moved the ad to ImageMatrix.h , everything will compile. However, I had to use dynamic_cast to make it work; it is right? If I leave, 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 = &image; return *dynamic_cast<T*>(imagePtr); } 

Update 6:

Referring to update 5 when I do not use dynamic_cast ...

 template<class T> T ImageMatrix::GetRotatedCopy(VDouble angle) { ImageMatrix image = _getRotatedCopy(angle); ImageMatrix *imagePtr = &image; //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 this 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.

+6
c ++ c # templates
source share
5 answers

Yes, you were very close, try the following:

 class FooBar1 { public: template<class T> T Foo(); }; class FooBar2 : public FooBar1 { }; template<class T> T FooBar1::Foo() { return T(); } int main() { FooBar1 fb1; FooBar2 fb2 = fb1.Foo<FooBar2>(); } 

The problem you are facing is that you specify the return type FooBar1::Foo() as FooBar2 , you should have it as simply T

If you want to do certain things for FooBar2, you can specialize in FooBar2:

 template<> FooBar2 FooBar1::Foo<FooBar2>() { return FooBar2(); } 

Edit: It looks like you are having problems with the compiler without finding a definition for your GetRotatedCopy template. C ++ templates are quite complex, and it is common practice to include the entire implementation of the template in the header file. You can try the following:

 class ImageMatrix : public VImage { public: // ... various functions ... template<class T> T GetRotatedCopy(VDouble angle) { // ... create a new instance of ImageMatrix and return it. } }; 

Edit: I can’t find the gcc documentation, but here the Microsoft documentation is in an explicit instance of the templates and libraries, it gives a little idea of ​​what is happening, you probably want to either include the implementation in the header, as I suggested earlier, or call GetRotatedCopy in library, or execute it explicitly in the library. See veefu answer below for syntax.

The reason this differs from C # is because C ++ templates, unlike C #, actually create a whole new class / function for each combination of template parameter combinations. for example, vector<int> is a completely different class (with a different set of compiled methods) than vector<string> . See Kevin's answer for a better explanation.

As for the error, when you are not using the template, this does not really tell you much, because until you create an instance of the template, it will not

RE Update 5,6,7

Your dynamic_cast will not work, you can only use it if the pointer actually points to an instance of the class you are producing. (It works similarly to the as operator in C #).

Now I suspect you need CRTP . You start with an instance of ImageFilter and want to use the base class method on it and return a new copy of ImageFilter. Try something in this direction:

 template <class T> class ImageMatrix { public: T GetRotatedMatrix() { return T(); } }; class ImageFilter : public ImageMatrix<ImageFilter> { }; int main() { ImageFilter filterPrototype; ImageFilter otherFilter = filterPrototype.GetRotatedMatrix(); } 

Otherwise, if you really want to start with ImageMatrix and convert it to ImageFilter, you will have to add a constructor to ImageFilter that accepts ImageMatrix.

+11
source share

Adding to what veefu said, templates in C ++ are not like C # generators (the topic is marked as C #, so I assume you are comparing them a bit). In C ++, the actual code is not generated at runtime, but only at compile time, so if you have a class with a template in it, you must either have it in the header file, or you must instantiate EXACTLY what instances it exists. otherwise, you (as a rule) will get a linker error, because it cannot find what you are looking for, because it has never been done. When creating templates, the compiler actually makes as many β€œcopies” of your template class as you created the various β€œviews” of the class. Therefore, from STL, if you have Vector<int> , Vector<String> and Vector<char> , the compiler really outputs the code for 3 different classes. This is why template classes are almost always defined in header files and not in compiled libraries, because the compiler must generate what you use.

This is different from Generics in C # (and Java IIRC), where references are used, and you can only use what you specify to inherit a generic name or object. You need to declare that something implements IComparable in order to use any of these methods or any other restrictions that you have imposed on it. Essentially, when you use a generic one, this is a type-compilation trick, but not ACTUALLY compiled in a class. C ++ differs in that if you have a class with a field template, then depending on the size of this class, the resulting class will be larger or smaller, which affects the actual size of the object.

Hope this made some sense, although it was a bit long. I really did not know that you could do boilerplate functions, as mch showed.

+4
source share

The error you get may be caused by other problems in your sample code. Here is an example of template members and free functions that compile perfectly with g ++.

 // Don't want the entire class to be generic. //template<class T> class FooBar1 { public: template<class T> T Foo(); }; class FooBar2 : public FooBar1 { }; template<class T> T FooBar1::Foo() { return T(); } template <class T> T FreeFunction() { return T(); } int main() { FooBar1 fb1; FooBar2 fb2 = fb1.Foo<FooBar2>(); FooBar2 fb3 = FreeFunction<FooBar2>(); } 

In C ++, you usually include both the declaration and the definition of template classes in the header file. This ensures that the compiler generates code for the template function or class with specific template arguments populated at compile time.

You can put the declaration in the header file and the definition in the cpp file. This works great if you have a small number of known types that you templates. You must explicitly create an instance of the class or function in the cpp file with the types that you expect from it so that the compiler can correctly generate the code.

Header file:

 template <class T> T DoSomething(); 

Cpp file:

 template <class T> T DoSomething(); { return T(); } template FooBar1 DoSomething<FooBar1>(); template FooBar2 DoSomething<FooBar2>(); 
+3
source share

Updates 3 and 4 give it away, I think, but it's hard to say without knowing the layout of your project.

If you are trying to open templates from the library for calling from another place, you need to either

A: include template definition in your library header file

or

B: explicitly create an instance of the template code in the library so that later code has an implementation to reference

 template<class T> T ImageMatrix::GetRotatedCopy(VDouble angle) { // ... create a new instance of ImageMatrix and return it. } // Add the following line template ImageFilter ImageMatrix::GetRotatedCopy<ImageFilter>(VDouble); 

I think this should solve the problem.

+1
source share

Maybe that

 template<class T> T FooBar1::Foo() { return something; } 

is in a .cpp file other than the calling file? If so, it will lead to your error. The implementation should be available in one compilation module (.cpp file + all of its included) as a call.

So, usually the implementation of the templates is placed in the header file where the ad lies.

You have to check it out.

+1
source share

All Articles