.net C++;与C&x2B+/CLI:虚拟功能参数的常量限定
[以下所有内容都是使用Visual Studio 2008 SP1测试的] <>在C++中,参数类型的const限定不影响函数的类型(83.5/3:“删除任何修改参数类型的CV限定符”) 例如,在下面的类层次结构中,.net C++;与C&x2B+/CLI:虚拟功能参数的常量限定,.net,visual-c++,c++-cli,virtual-functions,.net,Visual C++,C++ Cli,Virtual Functions,[以下所有内容都是使用Visual Studio 2008 SP1测试的] 在C++中,参数类型的const限定不影响函数的类型(83.5/3:“删除任何修改参数类型的CV限定符”) 例如,在下面的类层次结构中,Derived::Foo覆盖Base::Foo: struct Base { virtual void Foo(const int i) { } }; struct Derived : Base { virtual void Foo(int i) { } }; 考虑C
Derived::Foo
覆盖Base::Foo
:
struct Base
{
virtual void Foo(const int i) { }
};
struct Derived : Base
{
virtual void Foo(int i) { }
};
考虑C++/CLI中的类似层次结构:
ref class Base abstract
{
public:
virtual void Foo(const int) = 0;
};
ref class Derived : public Base
{
public:
virtual void Foo(int i) override { }
};
如果我随后创建派生的
的实例:
int main(array<System::String ^> ^args)
{
Derived^ d = gcnew Derived;
}
int main(数组^args)
{
派生^d=gcnew派生;
}
它编译时没有错误或警告。当我运行它时,它抛出以下异常,然后终止:
ClrVirtualTest.exe中发生类型为“System.TypeLoadException”的未处理异常
其他信息:“派生”类型中的方法“Foo”没有实现
该异常似乎表明参数的const限定确实会影响C++/CLI中函数的类型(或者,至少它会以某种方式影响重写)。但是,如果我注释掉包含Derived::Foo
定义的行,编译器将报告以下错误(在main
中实例化Derived
实例的行上):
错误C2259:“派生”:无法实例化抽象类
如果我将常量限定符添加到Derived::Foo
的参数中,或者从Base::Foo
的参数中删除常量限定符,则它编译并运行时不会出错
我认为,如果参数的const限定影响函数的类型,那么如果派生类虚拟函数中参数的const限定与基类虚拟函数中参数的const限定不匹配,我应该得到这个错误
如果我将Derived::Foo
的参数类型从int
更改为double
,我会得到以下警告(除了上述错误,C2259):
警告C4490:“覆盖”:不正确使用覆盖说明符;'派生::Foo'与基ref类方法不匹配
因此,我的问题是,函数参数的常量限定是否会影响C++/CLI中函数的类型?如果是这样的话,为什么要编译它,为什么没有错误或警告?如果没有,为什么会抛出异常?嗯,这是一个bug。常量修饰符通过modopt自定义修饰符发送到元数据中。遗憾的是,C++/CLI语言规则与CLI规则不匹配。CLI规范第7.1.1章规定: 自定义修饰符,使用modreq定义 (“所需修饰符”)和modopt (“可选修饰符”),类似于 自定义属性(§21),但以下情况除外: 修饰语是签名的一部分 而不是依附于 声明。每个修改者都有关联 中包含项的类型引用 签名 CLI本身应视需要进行处理 和可选的修饰符位于同一位置 态度。两个不同的签名 只有通过增加一个习惯 修改器(必需或可选)应 不被认为是匹配的。习俗 修改器对对象没有其他影响 VES的操作 因此,CLR说派生::Foo()不是重写,C++/CLI说是重写。CLR获胜
您可以在connect.microsoft.com上报告此错误,但这可能是浪费时间。我认为这种不相容是故意的。他们应该改变了C++语言的规则,但是肯定C++兼容性更重要。无论如何,CV修饰符都是一个难题,还有其他一些场景没有得到很好的支持,比如const指针指向const。无论如何,这不能在运行时强制执行,CLR不支持它。这是一个错误,并且不是特定于C++/CLI的
事实是,C++编译器应该剥离顶级的const /易失性。指针或引用的指向类型上只有const/volatile起作用。如果编译器做得正确,CLR就不会对正在发生的事情有发言权
顺便说一句,这是编译器使用/clr:pure生成的IL.class private abstract auto ansi beforefieldinit Base
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
.maxstack 1
L_0000: ldarg.0
L_0001: call instance void [mscorlib]System.Object::.ctor()
L_0006: ret
}
.method public hidebysig newslot abstract virtual instance void Foo(int32 modopt([mscorlib]System.Runtime.CompilerServices.IsConst)) cil managed
{
}
}
.class private auto ansi beforefieldinit Derived
extends Base
{
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
.maxstack 1
L_0000: ldarg.0
L_0001: call instance void Base::.ctor()
L_0006: ret
}
.method public hidebysig virtual instance void Foo(int32 i) cil managed
{
.maxstack 0
L_0000: ret
}
}
这绝对违反了James列出的关于删除顶级限定符的规则
C++/CLI规范的其他相关章节:
8.8.10.1功能覆盖
[剪报]
C++标准(Po.83.5/3)得到加强,如下:
转换后的参数类型和省略号的存在与否的结果列表是函数的参数类型列表
<> P>我相信,删除CV限定符的规则也适用于C++ +CLI,因为规范特别地调用了ISO标准C++的第83.5/3节。我查看了C++/CLI规范,但没有考虑查看CLI规范。由于没有太多使用.NET,我不知道CLI对const限定的支持有限,直到我开始深入研究这个问题。接下来,C++/CLI编译器是否有理由在生成IL时不能省略自定义修饰符?它不能,它从程序集元数据加载类定义。不是头文件。这些定义需要使用元数据功能进行编码。完全忽略CV限定符会使我们太不兼容。如果您只是#包含来自同一个库的头,而不是调用另一个DLL,那么听起来您会得到不同的行为。。。疯狂:(我不太确定。函数参数的cv限定只影响函数的定义;它不影响函数的类型,而放置c