2010-03-09 15 views
10

[Tutto il seguente è stata testata utilizzando Visual Studio 2008 SP1]C++ vs. C++/CLI: qualificazione Const di funzione virtuale parametri

In C++, qualificazione const dei tipi di parametri non influisce sul tipo di una funzione (8.3.5/3: "Ogni cv-qualificazione modifica di un tipo di parametro viene cancellato")

Così, per esempio, nel seguente gerarchia di classe, Derived::Foo override Base::Foo:

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

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

consideri una gerarchia simile a 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 { } 
}; 

Se poi creare un'istanza di Derived:

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

compila senza errori o avvisi. Quando l'eseguo, viene generata la seguente eccezione e quindi termina:

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

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

Tale eccezione sembra indicare che la qualifica const del parametro non influenza il tipo di funzione in C++/CLI (o, meno influisce in qualche modo sull'override). Tuttavia, se io commento la riga che contiene la definizione di Derived::Foo, il compilatore segnala il seguente errore (sulla linea in main dove viene istanziato l'istanza di Derived):

error C2259: 'Derived': cannot instantiate abstract class

Se aggiungo il qualificatore const alla parametro di Derived::Foo o rimuovere il qualificatore const dal parametro di Base::Foo, esso viene compilato ed eseguito senza errori.

Penso che se la qualifica const del parametro influenza il tipo della funzione, dovrei ottenere questo errore se la qualifica const del parametro nella funzione virtuale della classe derivata non corrisponde alla qualifica const del parametro in la funzione virtuale della classe base.

Se cambio il tipo di parametro Derived::Foo s' da un int ad un double, ottengo il seguente avviso (oltre all'errore di cui sopra, C2259):

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

Quindi, la mia domanda è, efficacemente, la qualifica const dei parametri di funzione influenza il tipo di funzione in C++/CLI? In tal caso, perché viene compilato e perché non ci sono errori o avvertimenti? In caso contrario, perché viene lanciata un'eccezione?

+0

Aggiornamento: ho segnalato questo come un bug su Microsoft Connect: https://connect.microsoft.com/VisualStudio/feedback/details/540788 (Ho copiato il link qui dalla discussione qui sotto poiché era sepolto nella conversazione) –

+0

Un membro del team di compilatore Microsoft C++/CLI ha confermato dal 21 marzo che si tratta di un bug e stanno esplorando l'impatto di una correzione sul codice esistente. –

+0

@ Ben: Grazie. Il difetto che ho presentato su Microsoft Connect è stato chiuso oggi come "non risolverà". "Ciao: grazie per aver segnalato questo problema. Sfortunatamente sulla base della nostra analisi della gravità di questo problema combinata con le nostre risorse limitate, non saremo in grado di risolvere questo problema nella prossima versione di Visual C++." Oh bene :-) –

risposta

8

Beh, è ​​un bug. I modificatori const vengono emessi nei metadati con il modificatore personalizzato modopt. Sfortunatamente, le regole del linguaggio C++/CLI non corrispondono alle regole CLI. Capitolo 7.1.1 della specifica CLI dice:

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.

Così, il CLR dice che Derived :: Foo() non è una sostituzione, C++/CLI dice che è. Il CLR vince.

È possibile segnalare il bug su connect.microsoft.com ma probabilmente è una perdita di tempo. Penso che questa incompatibilità sia stata intenzionale. Avrebbero dovuto cambiare le regole linguistiche per C++/CLI, ma sicuramente pensavano che la compatibilità con C++ fosse più importante.I modificatori di CV sono comunque un dolore, ci sono altri scenari che non sono ben supportati, puntatori const per const per uno. Questo non può essere applicato comunque in fase di esecuzione, il CLR non ha supporto per questo.

+0

Grazie. Avevo cercato le specifiche C++/CLI, ma non pensavo di cercare le specifiche CLI. Non avendo usato molto .NET, non conoscevo il supporto limitato della CLI per la qualifica const fino a quando non ho iniziato a indagare su questo problema. Come follow-up, c'è un motivo per cui il compilatore C++/CLI non può semplicemente lasciare fuori il modificatore personalizzato quando genera l'IL? –

+0

Non può, carica le definizioni di classe dai metadati dell'assieme. Non file di intestazione. Queste definizioni devono essere codificate utilizza le funzionalità dei metadati. Tralasciando completamente le qualifiche CV saremmo anche noi incompatibili. –

+0

Sembra che tu abbia un comportamento diverso se stai semplicemente includendo l'intestazione dalla stessa libreria e chiamando un'altra DLL ... Pazzesco :( –

3

È un bug e non è specifico per C++/CLI.

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

fatto è che il compilatore C++ dovrebbe togliere primo livello const/volatile. Solo const/volatile sul tipo puntato di un puntatore o riferimento. Se il compilatore lo ha fatto correttamente, il CLR non avrebbe avuto voce in capitolo su quello che stava succedendo.

BTW questo è il IL generato dal compilatore con/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 
    } 

} 

questo viola sicuramente la regola James elencato riguardante l'eliminazione di qualificatori di primo livello.

sezioni Ulteriori rilevanti della spec C++/CLI:

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.

Così mi sono portato a credere che la norma sulla cancellazione dei CV-qualificazioni applica a C++/CLI pure, perché le specifiche chiama specificamente fuori sezione 8.3.5/3 dello standard ISO C++.

+0

Per il codice nativo, quel bug è stato corretto, almeno a partire da VS2008 SP1 (il compilatore fornisce anche un avvertimento, C4373, "le versioni precedenti del compilatore non sovrascrivevano quando i parametri differivano solo dai qualificatori const/volatile"). Posso solo riprodurre il problema menzionato nel post originale con i tipi CLI (ad esempio, i tipi 'ref class'). –

+0

@James, concordo che il mio codice di riconfigurazione alla fine non causa problemi con VS2008 SP1 (la prima correzione non l'ha effettivamente corretto). E posso riproffare il tuo problema, dal momento che hai elencato un bug su Connect, lo convaliderò. –