我有一个包含实用程序函数的utils.h和utils.cpp,它们都被一个公共命名空间包围。当我直接将utils.h包含到项目中时,我可以访问所有这些函数。
utils.h:
namespace Utils
{
template <class T>
T interpolationLinear ( array<T,1>^ i_aData,
double i_dRatio)
{
array<T,1>^ aPart = gcnew array<T,1>(2);
aPart[0] = i_aData[0] * (1 - i_dRatio);
aPart[1] = i_aData[1] * (i_dRatio);
return aPart[0] + aPart[1];
}
...
double parseToDouble (System::String ^sValue); // defined in cpp
}
现在我正在创建几个托管DLL,实用程序函数应该成为其中之一。问题:我无法再访问DLL /程序集中的函数。似乎只有类可以构建DLL接口 然后我将命名空间更改为公共引用类并使函数保持静态,因此我可以访问某些函数:
utils.h:
public ref class Utils
{
template <class T>
static T interpolationLinear (array<T,1>^ i_aData,
double i_dRatio) // NO ACCESS
// like above
...
static double parseToDouble (System::String ^sValue); // CAN BE ACCESSED NOW
};
但为什么我不能调用模板函数?这里出了什么问题?
答案 0 :(得分:2)
您遇到了众所周知的C ++模板限制,它们没有外部链接。您可以通过将模板定义放入.h文件并在其他C ++项目中#include该文件来解决这个问题。那当然不能在这里工作,托管编译器不知道关于.h文件的bean,只有C ++编译器可以解析它们。
这可能是.NET Framework中的一个主要限制,它支持许多不同的语言,并且C ++和Java中使用的模板的类型擦除实现在实践中不能很好地工作。换句话说,像VB.NET这样的语言不能使用用C#等编写的模板。
所以他们采用了一种完全不同的方法,称为 reified generics 。与类型擦除的最大区别在于泛型方法是在运行时而不是编译时创建的。或者换句话说,托管编译器创建了该方法的“cookie-cutter”模型,它在运行时在实时编译时具体化。使用与任何语言相同的cookie-cutter代码,用MSIL表示,因此任何支持泛型的语言都可以调用它。
在C ++ / CLI中,您只需将模板替换为 generic 即可使用此类泛型。通常需要一些额外的语法来使用 where 关键字设置类型约束,但在您的情况下只需替换就足够了。
这实际上不起作用,您现在将发现特定于具体化泛型的限制。编译器会抱怨*运算符。这个运算符没有通用版本。根本问题是只有少数具有此运算符的实际类型。在.NET中只有int,long,float和double才有它。换句话说,当你调用方法时,你可以使用的实际T很少。
值得注意的是,您可以在代码中的 more 类型上实际使用该运算符。例如,它可以很好地乘以 byte 或 decimal 。但这需要进行非平凡的转换,字节必须首先提升到 int 。对于十进制,它需要发现该类型具有专用的operator*()
重载。这种非平凡的转换很容易通过知道类型的编译器来完成,但它们不能用编译器为通用方法生成的cookie-cutter代码表示。所以它根本就不受支持。
长话短说,你不能把这个方法变得通用。没有太大的真正问题,真的很少有你用过它的T,方法很小。所以只需提供方法的重载,问题就解决了。
答案 1 :(得分:1)
C ++中的模板和C#中的泛型是根本不同的概念,尽管它们提供了解决同一问题的方法。 C ++模板是编译时模板。这意味着您无法以在C#中导出泛型的方式导出它们。另一方面,C ++模板可以做很多C#泛型无法做到的事情,因为泛型需要确保它们可以为任何类型的T编译。
您创建了一个模板。这是一个C ++编译时间的东西,一旦编译就不会改变。您没有为特殊T编译,因此不会导出任何内容。
你可能想做的是像C#中的generic。对于泛型,请使用关键字generic
代替template
。
generic <class T> static T interpolationLinear (array<T,1>^ i_aData, double i_dRatio)