11

[以下はすべて、Visual Studio 2008 SP1 を使用してテストされています]

C++ では、パラメーター型の const 修飾は関数の型に影響しません (8.3.5/3: 「パラメーター型を変更する cv-qualifier は削除されます」)。

したがって、たとえば、次のクラス階層では、Derived::Foooverrides 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 { }
};

次に、のインスタンスを作成するとDerived:

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

エラーや警告なしでコンパイルされます。実行すると、次の例外がスローされて終了します。

タイプ 'System.TypeLoadException' の未処理の例外が ClrVirtualTest.exe で発生しました

追加情報: タイプ 'Derived' のメソッド 'Foo' ... には実装がありません。

その例外は、パラメーターの const 修飾がC ++/CLI の関数の型に影響を与える (または、少なくとも何らかの方法でオーバーライドに影響を与える) ことを示しているようです。ただし、 の定義を含む行をコメント アウトするとDerived::Foo、コンパイラは次のエラーを報告します (mainのインスタンスDerivedがインスタンス化されている行で)。

エラー C2259: 'Derived': 抽象クラスをインスタンス化できません

のパラメーターに const 修飾子を追加するDerived::Fooか、のパラメーターから const 修飾子を削除するBase::Fooと、エラーなしでコンパイルおよび実行されます。

パラメーターの const 修飾が関数の型に影響を与える場合、派生クラスの仮想関数のパラメーターの const 修飾が基本クラスの virtual のパラメーターの const 修飾と一致しない場合、このエラーが発生するはずだと思います関数。

Derived::Fooのパラメータの型をanintから aに変更するdoubleと、(前述のエラー C2259 に加えて) 次の警告が表示されます。

警告 C4490: 'override': オーバーライド指定子の使用が正しくありません。'Derived::Foo' は基本参照クラス メソッドと一致しません

したがって、私の質問は、事実上、関数パラメーターの const 修飾が C++/CLI の関数の型に影響するかどうかです。もしそうなら、なぜこれはコンパイルされ、エラーや警告がないのはなぜですか? そうでない場合、なぜ例外がスローされるのですか?

4

2 に答える 2

9

まあ、それはバグです。const 修飾子は、modopt カスタム修飾子を使用してメタデータに出力されます。残念ながら、C++/CLI 言語の規則は CLI の規則と一致しません。CLI 仕様の 7.1.1 章には次のように書かれています。

modreq (「必須の修飾子」) および modopt (「オプションの修飾子」) を使用して定義されたカスタム修飾子は、カスタム属性 (§21) に似ていますが、修飾子は宣言に添付されるのではなく、署名の一部である点が異なります。各修飾子は、型参照を署名内の項目に関連付けます。

CLI 自体は、必須およびオプションの修飾子を同じ方法で処理します。カスタム修飾子 (必須またはオプション) の追加のみが異なる 2 つの署名は、一致するとは見なされません。カスタム修飾子は、VES の操作に他の影響を与えません。

したがって、CLR は Derived::Foo() はオーバーライドではないと言いますが、C++/CLI はオーバーライドだと言います。CLRが勝ちます。

connect.microsoft.com でバグを報告できますが、おそらく時間の無駄です。この非互換性は意図的だったと思います。C++/CLI の言語規則を変更する必要がありましたが、C++ の互換性がより重要であると考えたのは確かです。とにかく CV 修飾子は苦痛です。十分にサポートされていないシナリオが他にもあります。その 1 つは const への const ポインターです。いずれにせよ、これを実行時に強制することはできません。CLR はそれをサポートしていません。

于 2010-03-09T23:19:28.790 に答える
3

これはバグであり、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. 派生クラス関数は、関数修飾子オーバーライドを使用して、同じ名前、パラメーターの型リスト、および cv 修飾を持つ基本クラスの仮想関数を明示的にオーバーライドし、そのような基本クラスの仮想関数が存在しない場合、プログラムは不正な形式になります。

12.3 宣言子の型

C++ 標準 (§8.3.5/3) は次のように拡張されます。
変換されたパラメーターの型の結果のリストと省略記号の有無は、関数のパラメーター型リストです。

そのため、cv 修飾子の削除に関する規則は C++/CLI にも適用されると考えるようになりました。これは、仕様が ISO 標準 C++ のセクション 8.3.5/3 を明確に示しているためです。

于 2010-03-10T20:12:53.477 に答える