简体   繁体   English

C ++是否支持单个泛型方法而不是泛型类?

[英]Does C++ support individual generic methods rather than generic classes?

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>() 未定义的引用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# ... 为了实现我想要实现的目标,我在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++? 有什么方法可以做类似于C ++的东西吗?

Update 1: 更新1:

Just corrected some minor syntax details (I meant to make Foo public, and return T, not FooBar2). 刚刚纠正了一些小的语法细节(我的意思是让Foo公开,然后返回T,而不是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: 更新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). Josh,这是实际的源代码(嗯,我认为是相关的,anwyay - 如果你认为我已经跳过了重要的一点,请告诉我)。

// 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)' /home/nick/Projects/ViMRID/vimrid/Debug/libvimrid.so:vimrid :: imaging :: processing :: ImageFilter vimrid :: imaging :: ImageMatrix :: GetRotatedCopy(double)'的未定义引用

Update 3: 更新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: 更新4:

When I comment out the implementation line (ImageFilter filter2 = filterPrototype...) it builds fine, so it seems to be this line thats causing it... 当我注释掉实现行(ImageFilter filter2 = filterPrototype ...)时,它构建得很好,所以看起来就是这一行导致它......

Update 5 (Solved?): 更新5(已解决?):

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. :)模板定义必须与声明一起在标题中(对吗?) - 所以现在我已将声明移到ImageMatrix.h ,所有内容都会编译。 However, I've had to use dynamic_cast to get it working; 但是,我必须使用dynamic_cast来使其正常工作; 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 = &image;
    return *dynamic_cast<T*>(imagePtr);
}

Update 6: 更新6:

Refering to update 5, when I don't use dynamic_cast... 参考更新5,当我不使用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: 更新7:

Also, if I don't use all of that smelly code in update 6... 另外,如果我在更新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中相同的错误。

Yup, you were pretty close, try this: 是的,你非常接近,试试这个:

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 issue you were having is that you were specifying the return type of FooBar1::Foo() as FooBar2 , you should have it as just T . 您遇到的问题是您将FooBar1::Foo()的返回类型指定为FooBar2 ,您应该将其设置为T

If you want to do specific things for FooBar2, you can specialize the on FooBar2: 如果你想为FooBar2做特定的事情,你可以专门研究FooBar2:

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

Edit: It sounds like you are having issues with the compiler not finding the definition for your templated GetRotatedCopy. 编辑:听起来你在编译器没有找到模板化GetRotatedCopy的定义时遇到问题。 Templates in C++ are rather finicky, and the usual practice is to put the entire template implementation in a header file. C ++中的模板相当挑剔,通常的做法是将整个模板实现放在头文件中。 You might try this: 你可以试试这个:

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's microsoft's documentation on explicit template instantiation and libraries, it gives a bit of an idea of what's happening. 编辑:我找不到gcc文档,但这里是microsoft关于显式模板实例化和库的文档 ,它给出了一些关于发生了什么的想法。 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. 您可能希望按照我之前的建议在头中包含实现,或者在库中调用GetRotatedCopy,或者在库中显式实例化它。 See veefu's answer below for the syntax. 有关语法,请参阅下面的veefu答案

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. 这与C#的不同之处在于,与C#不同,C ++中的模板实际上为每个不同的模板参数组合创建了一个全新的类/函数。 eg vector<int> is a completely different class (with a different set of compiled methods) than vector<string> . 例如, vector<int>是一个完全不同的类(使用不同的编译方法集)而不是vector<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 RE更新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. 您的dynamic_cast不起作用,只有当指针实际指向您要转换的类的实例时,才能使用它。 (It works similar to the as operator in C#). (它的工作方式类似于C#中的as运算符)。

I suspect now, that what you are wanting is the CRTP . 我现在怀疑,你想要的是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. 您从ImageFilter的实例开始,并希望在其上使用基类方法,并获取ImageFilter的新副本。 Try something along these lines: 尝试这些方面的东西:

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 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. 否则,如果您真的想从ImageMatrix开始并将其转换为ImageFilter,则必须在ImageFilter上添加一个构造函数,该构造函数采用ImageMatrix。

Adding on to what veefu said, templates in C++ are NOT like C# generics (the topic is tagged as C# so I assume you are comparing them somewhat). 除了veefu所说的,C ++中的模板不像C#泛型(主题被标记为C#,所以我假设你在某种程度上比较它们)。 In C++, the actual code is not generated at runtime, but only at compile-time, so that if you have a class with a template in it, you either must have it in a header file, or you must instantiate EXACTLY which instances of it exist, or else you'll (usually) get a linker error because it can't FIND what you're looking for, because it was never actually made. 在C ++中,实际代码不是在运行时生成的,而是仅在编译时生成的,因此如果您有一个包含模板的类,您必须将它放在头文件中,或者您必须实例化实例化哪些实例它存在,否则你(通常)会得到一个链接器错误,因为它无法找到你正在寻找的东西,因为它实际上从未实现过。 When doing templates, the compiler actually makes as many "copies" of your template class as you have instantiated different "kinds" of the class. 在进行模板时,编译器实际上会创建模板类的“副本”,因为您已经实例化了类的不同“种类”。 So from the STL, if you have Vector<int> , Vector<String> , and Vector<char> , the compiler actually outputs the code for 3 different classes. 所以从STL,如果你有Vector<int>Vector<String>Vector<char> ,编译器实际上输出3个不同类的代码。 This is why templated classes are almost-always defined in header files and not in compiled libraries, because the compiler needs to generate what you're using. 这就是为什么模板化的类几乎总是在头文件中而不是在编译库中定义的原因,因为编译器需要生成您正在使用的内容。

This is in contrast to Generics in C# (and Java IIRC) where references are used, and you can only use what you have specified the generic to be inherited from, or from object. 这与使用引用的C#(和Java IIRC)中的Generics形成对比,并且您只能使用已指定要从其继承的泛型或从对象继承的内容。 You need to declare that something implements IComparable to use any of the methods from that, or whatever other restrictions you put on it. 您需要声明某些东西实现IComparable以使用其中的任何方法,或者您对其施加的任何其他限制。 Essentially, when you use a generic, it's a compile-time-trick to ensure type safety, but doesn't ACTUALLY compile-in the class. 基本上,当你使用泛型时,它是一个编译时技巧,以确保类型安全,但不是在课堂上实际编译。 C++ is different, in that if you have a class with a templated field, then depending on the size of that class, the resultant class will be bigger or smaller, which affects the actual size of the object. C ++是不同的,因为如果你有一个带有模板化字段的类,那么根据该类的大小,结果类会更大或更小,这会影响对象的实际大小。

I hope that made some sense, though it was a bit long. 我希望这有点道理,虽然它有点长。 I actually didn't know you could do templated functions as mch showed. 我实际上并不知道你可以像mch所展示的那样做模板化的功能。

The error you are getting may be due to other problems in your sample code. 您获得的错误可能是由于示例代码中的其他问题。 Here is an example of templated member and free functions that compiles fine with G++. 下面是一个模板化成员和自由函数的例子,可以用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 generally include both the declaration and definition of templated classes in the header file. 在C ++中,通常在头文件中包含模板化类的声明和定义。 Doing this ensures that the compiler generates code for the templated function or class with the specific template arguments filled in at compile time. 这样做可确保编译器为模板化函数或类生成代码,并在编译时填充特定模板参数。

It is possible to put the declaration in a header file and the definition in a cpp file. 可以将声明放在头文件中,将定义放在cpp文件中。 This works fine if you have a small number of known types you are templating. 如果您有少量已模仿的已知类型,则此方法可以正常工作。 It is necessary to explicitly instantiate the class or function in the cpp file with the types you expect it to be used with, so that the compiler can generate the code correctly. 有必要使用您希望与之一起使用的类型在cpp文件中显式实例化类或函数,以便编译器可以正确生成代码。

Header file: 头文件:

template <class T>
T DoSomething();

Cpp file: Cpp文件:

template <class T>
T DoSomething();
{
    return T();
}

template FooBar1 DoSomething<FooBar1>();
template FooBar2 DoSomething<FooBar2>();

Update 3 and 4 give it away, I think, but it's difficult to tell without knowing your project layout. 我认为,更新3和4会将其丢弃,但在不知道项目布局的情况下很难说清楚。

If you're trying to expose templates from a library to be called from another location, you have to either 如果您尝试从要从其他位置调用的库中公开模板,则必须执行此操作

A: include the template definition in the header file of your library 答:在库的头文件中包含模板定义

or 要么

B: explicitly instantiate the template code in the library, so that later code will have an implementation to link to B:显式实例化库中的模板代码,以便后面的代码将有一个实现链接到

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 that should resolve the problem. 我认为应该解决问题。

May it be that 可能就是这样

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

is in a .cpp file other than the calling file? 是否在调用文件以外的.cpp文件中? If so this would lead to your error. 如果是这样会导致您的错误。 The implementation must be available in the same compilation unit (.cpp file + all its includes) as the call. 该实现必须在同一编译单元(.cpp文件及其所有包含的内容)中作为调用提供。

So normally template implementations are put in the header file where the declaration lies. 因此,通常将模板实现放在声明所在的头文件中。

Sou you should check this. 你应该检查一下。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM