[Все следующее было протестировано с помощью Visual Studio 2008 SP1]
В C++ квалификация константы типов параметра не влияет на тип функции (8.3.5/3: "Любой спецификатор условной цены, изменяющий тип параметра, удален"),
Так, например, в следующей иерархии классов, 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 { }
};
Если я затем создаю экземпляр Derived
:
int main(array<System::String ^> ^args)
{
Derived^ d = gcnew Derived;
}
это компилирует без ошибок или предупреждений. Когда я выполняю его, это выдает следующее исключение и затем завершается:
Необработанное исключение типа 'Система. TypeLoadException' произошел в ClrVirtualTest.exe
Дополнительная информация: Метод 'Нечто' в типе, 'Полученном'..., не имеет реализации.
То исключение, кажется, указывает, что квалификация константы параметра действительно влияет на тип функции в C++ / CLI (или, по крайней мере, это влияет на переопределение в некотором роде). Однако, если я комментирую строку, содержащую определение Derived::Foo
, компилятор сообщает о следующей ошибке (на строке в main
где экземпляр Derived
инстанцирован):
ошибка C2259: 'Полученный': не может инстанцировать абстрактного класса
Если я добавляю спецификатор константы к параметру Derived::Foo
или удалите спецификатор константы из параметра Base::Foo
, это компилирует и работает без ошибок.
Я думал бы, что, если квалификация константы параметра влияет на тип функции, я должен получить эту ошибку, если квалификация константы параметра в виртуальной функции производного класса не соответствует квалификации константы параметра в виртуальной функции базового класса.
Если я изменяю тип Derived::Foo
параметр от int
к a double
, Я получаю соблюдающее предупреждение (в дополнение к вышеупомянутой ошибке, C2259):
предупреждение C4490: 'переопределение': неправильное использование спецификатора переопределения; 'Полученный:: Нечто' не соответствует основе касательно метода класса
Так, мой вопрос, эффективно, делает квалификацию константы влияния параметров функции тип функции в C++ / CLI? Если так, почему это компилирует и почему не там никакие ошибки или предупреждения? В противном случае, почему исключение выдается?
Что ж, это ошибка. Модификаторы const передаются в метаданные с помощью настраиваемого модификатора modopt. К сожалению, правила языка C ++ / CLI не соответствуют правилам CLI. В главе 7.1.1 спецификации CLI говорится:
Пользовательские модификаторы, определенные с помощью modreq («обязательный модификатор») и modopt («необязательный модификатор»), аналогичны {{1 }} настраиваемые атрибуты (§21), за исключением того, что модификаторы являются частью подписи , а не прикрепляются к объявлению . Каждый модификатор связывает ссылку на тип с элементом в сигнатуре .
Сам CLI должен обрабатывать обязательные и необязательные модификаторы одинаково . Две подписи, которые отличаются только добавлением специального модификатора (обязательного или необязательного), не считаются совпадающими. Пользовательские модификаторы не имеют другого влияния на работу VES.
Итак, CLR говорит, что Derived :: Foo () не является переопределением, а C ++ / CLI говорит, что это так. CLR побеждает.
Вы можете сообщить об ошибке на сайте connect.microsoft.com, но, вероятно, это пустая трата времени. Я думаю, эта несовместимость была намеренной. Им следовало изменить языковые правила для C ++ / CLI, но определенно считали совместимость с C ++ более важной. В любом случае модификаторы CV - это боль, есть другие сценарии, которые не очень хорошо поддерживаются, например, указатели const на const. В любом случае это не может быть применено во время выполнения, среда CLR не поддерживает этого.
Это ошибка, не относящаяся к C ++ / CLI.
Фактически, компилятор C ++ предполагается убрать верхний уровень 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
}
}
Это определенно нарушает указанное Джеймсом правило, касающееся удаления квалификаторов верхнего уровня.
Дополнительные соответствующие разделы спецификации C ++ / CLI:
8.8.10.1 Переопределение функции
[snip]
- Функция производного класса явно переопределяет виртуальную функцию базового класса с тем же именем, списком типов-параметров и cv- квалификация, используя переопределение модификатора функции, при этом программа плохо сформирована, если такая виртуальная функция базового класса не существует
12.3 Типы деклараторов
Стандарт C ++ (§8.3.5 / 3) дополняется следующим образом:
Результирующий список преобразованных типов параметров и наличие или отсутствие многоточия - это список типов параметров функции.
Итак, я пришел к выводу, что правило удаления cv-квалификаторов применимо и к C ++ / CLI, потому что в спецификации специально упоминается раздел 8.3.5 / 3 стандарта ISO C ++.