C++与 C++/CLI:虚函数参数的 const 限定

发布于 2024-08-24 16:39:10 字数 1590 浏览 9 评论 0原文

[以下所有内容均使用 Visual Studio 2008 SP1 进行测试]

在 C++ 中,参数类型的 const 限定不会影响函数的类型(8.3.5/3:“删除修改参数类型的任何 cv 限定符”)

因此,例如,在以下类层次结构中,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;
}

编译时没有错误或警告。当我运行它时,它抛出以下异常然后终止:

ClrVirtualTest.exe 中发生“System.TypeLoadException”类型的未处理异常

其他信息:“Derived”类型中的方法“Foo”...没有实现。

该异常似乎表明参数的 const 限定确实影响 C++/CLI 中函数的类型(或者,至少它以某种方式影响重写)。但是,如果我注释掉包含 Derived::Foo 定义的行,编译器会报告以下错误(在 main 的行上,其中 的实例派生被实例化):

错误 C2259:“派生”:无法实例化抽象类

如果我将 const 限定符添加到 Derived::Foo 的参数或从 Base::Foo 的参数中删除 const 限定符code>,它编译并运行没有错误。

我认为如果参数的 const 限定影响函数的类型,如果派生类虚函数中参数的 const 限定与基类 virtual 中参数的 const 限定不匹配,我应该得到此错误功能。

如果我将 Derived::Foo 的参数类型从 int 更改为 double,我会收到以下警告(除了上述错误,C2259):

警告 C4490:“覆盖”:不正确使用覆盖说明符; “Derived::Foo”与基引用类方法不匹配

所以,我的问题是,函数参数的 const 限定实际上是否会影响 C++/CLI 中函数的类型?如果是这样,为什么会编译并且没有错误或警告?如果不是,为什么会抛出异常?

[All of the following was tested using Visual Studio 2008 SP1]

In C++, const qualification of parameter types does not affect the type of a function (8.3.5/3: "Any cv-qualifier modifying a parameter type is deleted")

So, for example, in the following class hierarchy, Derived::Foo overrides Base::Foo:

struct Base
{
    virtual void Foo(const int i) { }
};

struct Derived : Base
{
    virtual void Foo(int i) { }
};

Consider a similar hierarchy in 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 { }
};

If I then create an instance of Derived:

int main(array<System::String ^> ^args)
{
    Derived^ d = gcnew Derived;
}

it compiles without errors or warnings. When I run it, it throws the following exception and then terminates:

An unhandled exception of type 'System.TypeLoadException' occurred in ClrVirtualTest.exe

Additional information: Method 'Foo' in type 'Derived'...does not have an implementation.

That exception seems to indicate that the const qualification of the parameter does affect the type of the function in C++/CLI (or, at least it affects overriding in some way). However, if I comment out the line containing the definition of Derived::Foo, the compiler reports the following error (on the line in main where the instance of Derived is instantiated):

error C2259: 'Derived': cannot instantiate abstract class

If I add the const qualifier to the parameter of Derived::Foo or remove the const qualifier from the parameter of Base::Foo, it compiles and runs with no errors.

I would think that if the const qualification of the parameter affects the type of the function, I should get this error if the const qualification of the parameter in the derived class virtual function does not match the const qualification of the parameter in the base class virtual function.

If I change the type of Derived::Foo's parameter from an int to a double, I get the following warning (in addition to the aforementioned error, C2259):

warning C4490: 'override': incorrect use of override specifier; 'Derived::Foo' does not match a base ref class method

So, my question is, effectively, does the const qualification of function parameters affect the type of the function in C++/CLI? If so, why does this compile and why are there no errors or warnings? If not, why is an exception thrown?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

风柔一江水 2024-08-31 16:39:10

嗯,这是一个错误。 const 修饰符通过 modopt 自定义修饰符发送到元数据中。不幸的是,C++/CLI 语言规则与 CLI 规则不匹配。 CLI 规范第 7.1.1 章说:

自定义修饰符,使用 modreq 定义
(“必需的修饰符”)和 modopt
(“可选修饰符”),类似于
自定义属性(§21),除了
修饰符是签名的一部分
而不是被依附于
声明。每个修饰符关联
带有项目的类型引用
签名。

CLI 本身应处理所需的
和相同的可选修饰符
方式。两个不同的签名
仅通过添加自定义
修饰符(必需或可选)应
不被视为匹配。风俗
修饰符没有其他影响
VES 的操作。

因此,CLR 表示 Derived::Foo() 不是重写,而 C++/CLI 表示它是重写。 CLR 获胜。

您可以在 connect.microsoft.com 上报告该错误,但这可能会浪费时间。我认为这种不兼容是故意的。他们应该改变 C++/CLI 的语言规则,但肯定认为 C++ 兼容性更重要。 CV 修饰符无论如何都是一种痛苦,还有其他一些没有得到很好支持的场景,其中一个就是指向 const 的 const 指针。无论如何,这不能在运行时强制执行,CLR 不支持它。

Well, it's a bug. The const modifiers is emitted into the metadata with the modopt custom modifier. Unfortunately, the C++/CLI language rules do not match the CLI rules. Chapter 7.1.1 of the CLI spec says:

Custom modifiers, defined using modreq
(“required modifier”) and modopt
(“optional modifier”), are similar to
custom attributes (§21) except that
modifiers are part of a signature
rather than being attached to
adeclaration. Each modifer associates
a type reference with an item in the
signature.

The CLI itself shall treat required
and optional modifiers in the same
manner. Two signatures that differ
only by the addition of a custom
modifier (required or optional) shall
not be considered to match. Custom
modifiers have no other effect on the
operation of the VES.

So, the CLR says that Derived::Foo() is not a override, C++/CLI says it is. The CLR wins.

You could report the bug at connect.microsoft.com but it probably a waste of time. I think this incompatibility was intentional. They should have changed the language rules for C++/CLI but surely thought C++ compatibility to be more important. CV modifiers are a pain anyway, there are other scenarios that are not well supported, const pointers to const for one. This cannot be enforced at runtime anyway, the CLR has no support for it.

昵称有卵用 2024-08-31 16:39:10

这是一个错误,并且不是 C++/CLI 特有的。

https://connect.microsoft.com/VisualStudio/feedback/details/100917/argument-const-ness-is-part-of-member-function-type-signature

事实上,C++ 编译器应该去掉顶级 const/volatile。只有指针或引用的指向类型上的 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 函数重写

[截图]

  1. 派生类函数通过使用函数修饰符 override 显式重写具有相同名称、参数类型列表和 cv 限定的基类虚函数,如果没有此类基类虚函数,则程序格式错误函数存在

12.3 声明符类型

C++ 标准 (§8.3.5/3) 得到增强,如下:
转换后的参数类型的结果列表以及省略号是否存在是函数的参数类型列表。

因此,我相信删除 cv 限定符的规则也适用于 C++/CLI,因为该规范特别引用了 ISO 标准 C++ 的第 8.3.5/3 节。

It's a bug, and it's not specific to C++/CLI.

https://connect.microsoft.com/VisualStudio/feedback/details/100917/argument-const-ness-is-part-of-member-function-type-signature

Fact is, the C++ compiler is supposed to strip off top-level const/volatile. Only const/volatile on the pointed-to type of a pointer or reference matters. If the compiler did that correctly, the CLR wouldn't have a say in what's going on.

BTW this is the IL generated by the compiler with /clr:pure

.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 
    }

}

This definitely violates the rule James listed concerning deletion of top-level qualifiers.

Further relevant sections of the C++/CLI spec:

8.8.10.1 Function overriding

[snip]

  1. A derived class function explicitly overrides a base class virtual function having the same name, parameter-type-list, and cv-qualification, by using the function modifier override, with the program being ill-formed if no such base class virtual function exists

12.3 Declarator types

The C++ Standard (§8.3.5/3) is augmented, as follows:
The resulting list of transformed parameter types and the presence or absence of the ellipsis is the function’s parameter-type-list.

So I am led to believe that the rule on deletion of cv-qualifiers applies to C++/CLI as well, because the spec specifically calls out section 8.3.5/3 of ISO Standard C++.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文