Подтвердить что ты не робот

С++ против С++/CLI: определение констант виртуальных функций

[Все перечисленные ниже были протестированы с использованием Visual Studio 2008 SP1]

В С++ константная конфигурация типов параметров не влияет на тип функции (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) { }
};

Рассмотрим аналогичную иерархию в С++/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

Дополнительная информация: Метод "Foo" в типе "Производный"... не имеет реализации.

Это исключение, по-видимому, указывает на то, что константная спецификация параметра влияет на тип функции в С++/CLI (или, по крайней мере, это влияет на переопределение каким-либо образом). Однако, если я прокомментирую строку, содержащую определение Derived::Foo, компилятор сообщает следующую ошибку (в строке в main, где экземпляр Derived создается):

ошибка C2259: "Производится": не может создать абстрактный класс

Если я добавлю спецификатор const к параметру Derived::Foo или удалю классификатор const из параметра Base::Foo, он компилируется и запускается без ошибок.

Я бы подумал, что если константная характеристика параметра влияет на тип функции, я должен получить эту ошибку, если const-характеристика параметра в виртуальной функции производного класса не соответствует константной спецификации параметра в виртуальная функция базового класса.

Если изменить тип параметра Derived::Foo с int на double, я получаю следующее предупреждение (в дополнение к вышеупомянутой ошибке C2259):

предупреждение C4490: "переопределение": неправильное использование спецификатора переопределения; 'Derived:: Foo' не соответствует методу базового класса ref

Итак, мой вопрос заключается в том, влияет ли const-функция параметров функции на тип функции в С++/CLI? Если да, то почему это компилируется и почему нет ошибок или предупреждений? Если нет, почему выбрано исключение?

4b9b3361

Ответ 1

Ну, это ошибка. Модификаторы const выходят в метаданные с модификатором modopt. К сожалению, правила языка С++/CLI не соответствуют правилам CLI. В главе 7.1.1 спецификации CLI говорится:

Пользовательские модификаторы, определенные с помощью modreq ( "требуемый модификатор" ) и modopt ( "необязательный модификатор" ), аналогичны пользовательские атрибуты (§21), за исключением того, что модификаторы являются частью подписи а не привязываться к adeclaration. Каждый участник ссылка типа с элементом в подпись.

Сам CLI должен и необязательные модификаторы в том же манера. Две подписи, которые различаются только путем добавления пользовательского модификатор (обязательно или необязательно) должен не считаются совпадающими. изготовленный на заказ модификаторы не оказывают иного влияния на работа VES.

Итак, CLR говорит, что Derived:: Foo() не является переопределением, говорит С++/CLI. CLR выигрывает.

Вы можете сообщить об ошибке на сайте connect.microsoft.com, но это, вероятно, пустая трата времени. Я думаю, что эта несовместимость была преднамеренной. Они должны были изменить языковые правила для С++/CLI, но наверняка считали, что совместимость с С++ более важна. Модификаторы CV - это боль в любом случае, есть и другие сценарии, которые недостаточно хорошо поддерживаются, константные указатели на const для одного. В любом случае это не может быть применено во время выполнения, CLR не поддерживает его.

Ответ 2

Это ошибка, и она не специфична для С++/CLI.

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

Фактически, компилятор С++ должен отключить const-volatile верхнего уровня. Только const/volatile по указательному типу указателя или ссылки. Если компилятор выполнил это правильно, CLR не будет говорить о том, что происходит.

Кстати, это IL, сгенерированный компилятором с /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 
    }

}

Это определенно нарушает правило Джеймса, касающееся удаления квалификаторов верхнего уровня.

Другие соответствующие разделы спецификации С++/CLI:

8.8.10.1 Функция переопределения

[надрез]

  • Функция производного класса явно переопределяет виртуальную функцию базового класса, имеющую одно и то же имя, тип-тип-список и cv-квалификацию, с использованием переопределения модификатора функции при плохой форме программы, если такой базовый класс виртуальный функция существует

12.3 Типы деклараторов

Стандарт С++ (§8.3.5/3) дополняется следующим образом:
Результирующим списком преобразованных типов параметров и наличием или отсутствием эллипсиса являются функции parameter-type-list.

Итак, я убежден, что правило по удалению cv-квалификаторов относится и к С++/CLI, потому что спецификация специально вызывает раздел 8.3.5/3 стандарта ISO С++.