VC11. Фигня с виртуальными функциями.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 25.02.13 09:08
Оценка:
Привет всем.

Что-то я немного недоумеваю со следующего кода, который без проблем скомпилировался в VS2010 и VS2012.

////////////////////////////////////////////////////////////////////////////////
//class TCheck

class TCheck
{
 public:
  virtual void check()const=0;
};//class TCheck

////////////////////////////////////////////////////////////////////////////////
//class TAccessor

class TAccessor
{
 public:
  virtual const TCheck* get_check()const=0;
};//class TCheck

////////////////////////////////////////////////////////////////////////////////
//class TObject

class TObject:public TAccessor
{
 public:
  //Формально оно не должно же этой функцией реализовывать TAccessor::get_check
  virtual /*const*/ TCheck* get_check()const override;
};//class TObject

//------------------------------------------------------------------------
TCheck* TObject::get_check()const
{
 return 0;
}//get_check

////////////////////////////////////////////////////////////////////////////////

int main()
{
 TObject obj;

 return 0;
}//main

////////////////////////////////////////////////////////////////////////////////


На мой мутный взгляд — компилятор пропустил багу. Он же должен ругаться что TAccessor::get_check не реализован?

Или я чего-то не понимаю?
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: VC11. Фигня с виртуальными функциями.
От: Logot Украина  
Дата: 25.02.13 09:18
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Привет всем.


КД>Что-то я немного недоумеваю со следующего кода, который без проблем скомпилировался в VS2010 и VS2012.


КД>
КД>////////////////////////////////////////////////////////////////////////////////
КД>//class TCheck

КД>class TCheck
КД>{
КД> public:
КД>  virtual void check()const=0;
КД>};//class TCheck

КД>////////////////////////////////////////////////////////////////////////////////
КД>//class TAccessor

КД>class TAccessor
КД>{
КД> public:
КД>  virtual const TCheck* get_check()const=0;
КД>};//class TCheck

КД>////////////////////////////////////////////////////////////////////////////////
КД>//class TObject

КД>class TObject:public TAccessor
КД>{
КД> public:
КД>  //Формально оно не должно же этой функцией реализовывать TAccessor::get_check
КД>  virtual /*const*/ TCheck* get_check()const override;
КД>};//class TObject

КД>//------------------------------------------------------------------------
КД>TCheck* TObject::get_check()const
КД>{
КД> return 0;
КД>}//get_check

КД>////////////////////////////////////////////////////////////////////////////////

КД>int main()
КД>{
КД> TObject obj;

КД> return 0;
КД>}//main

КД>////////////////////////////////////////////////////////////////////////////////
КД>


КД>На мой мутный взгляд — компилятор пропустил багу. Он же должен ругаться что TAccessor::get_check не реализован?


КД>Или я чего-то не понимаю?


В сигнатуру метода на входит возвращаемое значение, поэтому в данном случае ругаться не должен
Re: VC11. Фигня с виртуальными функциями.
От: Erop Россия  
Дата: 25.02.13 09:20
Оценка: +1
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>На мой мутный взгляд — компилятор пропустил багу. Он же должен ругаться что TAccessor::get_check не реализован?

В смысле что попытка создать экземпляр абстрактного класса + override несуществующего метода базовового класса, ты хотел сказать?..

А почему, собственно?
Вообще-то в С++ перегрузка методов по типу возвращаемого значения допускается только для пользовательстких операторов преобразования к типу...

То есть всё что могло бы быть, так это запрет возвращать из TObject::get_check неконстантную ссылку. То есть ругаться должен был бы, на то, что тип возвращаемого значения в реализации не совпадает с типов возвращаемого значения в базе...

но тут есть такая штука, как ковариантность виртуальных функций, что ли, оно называется.

Ты можешь сделать так примерно:
struct Base {
    virtual ~Base() {}

    Base* Clone() const { return new Base( *this ); }
};

struct Der : Base {
    Der* Clone() const { return new Der( *this ); }
};
и всё при этом получится, потому, что С++ позволяет в виртуальном методе наследника заменять тип результата на совместимый...

КД>Или я чего-то не понимаю?


Я так думаю, что у тебя тот же случай, но боюсь, что это нестандарьтное расширение от MS, хотя и не уверен...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: VC11. Фигня с виртуальными функциями.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 25.02.13 09:32
Оценка:
Здравствуйте, Erop, Вы писали:

КД>>На мой мутный взгляд — компилятор пропустил багу. Он же должен ругаться что TAccessor::get_check не реализован?


E>А почему, собственно?

Я думаю, компилятор не должен игнорировать потерю константности у возвращаемого типа.

E>но тут есть такая штука, как ковариантность виртуальных функций, что ли, оно называется.


Про это я знаю. И даже эксплуатирую. И ругался что похожего нет в C#
Автор: Коваленко Дмитрий
Дата: 21.11.11
.

Здесь, я думаю, ситуация другая.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: VC11. Фигня с виртуальными функциями.
От: Erop Россия  
Дата: 25.02.13 09:47
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Я думаю, компилятор не должен игнорировать потерю константности у возвращаемого типа.

Так у этой функции сигнатура та же, другое только возвращаемое значение...

попробуй поменять в наследнике именно сигнатуру, например константность самого метода, и посмотри на что и как будет ругаться

КД>Здесь, я думаю, ситуация другая.

Почему? попробуй в наследнике вернуть int, например...

только можно попроще код-то замутить...

struct Base { virtual const Base* This() { return this; } };
struct Der : Base { Base* This() override { return this; } } testObj;
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: VC11. Фигня с виртуальными функциями.
От: rg45 СССР  
Дата: 25.02.13 09:53
Оценка: 10 (1)
Здравствуйте, Erop, Вы писали:

E>но тут есть такая штука, как ковариантность виртуальных функций, что ли, оно называется.

E>Я так думаю, что у тебя тот же случай, но боюсь, что это нестандарьтное расширение от MS, хотя и не уверен...

Штука эта называется ковариантностью возвращаемых типов. В стандарте (как в старом, так и в новом) она прописана в п.10.3 и разрешена только для классов:

The return type of an overriding function shall be either identical to the return type of the overridden function or covariant with the classes of the functions. If a function D::f overrides a function B::f, the return types of the functions are covariant if they satisfy the following criteria:
— both are pointers to classes, both are lvalue references to classes, or both are rvalue references to classes
— the class in the return type of B::f is the same class as the class in the return type of D::f, or is an unambiguous and accessible direct or indirect base class of the class in the return type of D::f
— both pointers or references have the same cv-qualification and the class type in the return type of D::f has the same cv-qualification as or less cv-qualification than the class type in the return type of B::f.


И похоже, расширение для этой фичи для встроенных типов сделано не только в msvc: http://ideone.com/MNzqzi
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: VC11. Фигня с виртуальными функциями.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 25.02.13 09:56
Оценка:
Здравствуйте, Erop, Вы писали:

E>Так у этой функции сигнатура та же, другое только возвращаемое значение...

E>попробуй поменять в наследнике именно сигнатуру, например константность самого метода, и посмотри на что и как будет ругаться

Ошибка 1 error C3668: TObject::get_check: метод со спецификатором переопределения "override" не переопределяет какие-либо методы базового класса D:\Users\Dima\Work\TestCode\compiler_test\test_015\source\main.cpp 25
Ошибка 2 error C2259: TObject: невозможно создать экземпляр абстрактного класса D:\Users\Dima\Work\TestCode\compiler_test\test_015\source\main.cpp 38


КД>>Здесь, я думаю, ситуация другая.

E>Почему? попробуй в наследнике вернуть int, например...

Ошибка 1 error C2555: TObject::get_check: возвращаемый тип перегруженной виртуальной функции отличается от "TAccessor::get_check" и не является ковариантным D:\Users\Dima\Work\TestCode\compiler_test\test_015\source\main.cpp 26
Ошибка 2 error C2556: TCheck *TObject::get_check(void) const: перегруженная функция отличается от "int *TObject::get_check(void) const" только возвращаемым типом D:\Users\Dima\Work\TestCode\compiler_test\test_015\source\main.cpp 30
Ошибка 3 error C2371: TObject::get_check: переопределение; различные базовые типы D:\Users\Dima\Work\TestCode\compiler_test\test_015\source\main.cpp 30


Что первая, что вторая ошибка — это нормальное положение вещей.

E>только можно попроще код-то замутить...

E>
struct Base { virtual const Base* This() { return this; } };
E>struct Der : Base { Base* This() override { return this; } } testObj;


Возможно. Я просто воспроизвел хоровод классов из реального проекта.

Удивительно, что я вообще это заметил. После перехода на VC я конкретно расслабился в отношении таких вещей. Но, видать, сапёр во мне еще не умер
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: VC11. Фигня с виртуальными функциями.
От: Erop Россия  
Дата: 25.02.13 09:57
Оценка:
Здравствуйте, rg45, Вы писали:

R>

R>— both pointers or references have the same cv-qualification and the class type in the return type of D::f has the same cv-qualification as or less cv-qualification than the class type in the return type of B::f.


R>И похоже, расширение для этой фичи для встроенных типов сделано не только в msvc: http://ideone.com/MNzqzi


Дык у ТС всё по стандарту жеж...
только понятие less cv-qualification довольно мутное, но в данном случае оно наверное же less?

А то, что можно не только классы, но и на разное удругое указатели ковариантить, так это да, вроде все умеют, благо бесплатно жеж...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: VC11. Фигня с виртуальными функциями.
От: Erop Россия  
Дата: 25.02.13 10:01
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

и посмотри на что и как будет ругаться
КД>

КД>Ошибка 1 error C3668: TObject::get_check: метод со спецификатором переопределения "override" не переопределяет какие-либо методы базового класса D:\Users\Dima\Work\TestCode\compiler_test\test_015\source\main.cpp 25
КД>Ошибка 2 error C2259: TObject: невозможно создать экземпляр абстрактного класса D:\Users\Dima\Work\TestCode\compiler_test\test_015\source\main.cpp 38


Бинго! А я что говорил?..

E>>Почему? попробуй в наследнике вернуть int, например...

КД>

КД>Ошибка 1 error C2555: TObject::get_check: возвращаемый тип перегруженной виртуальной функции отличается от "TAccessor::get_check" и не является ковариантным D:\Users\Dima\Work\TestCode\compiler_test\test_015\source\main.cpp 26


КД>Что первая, что вторая ошибка — это нормальное положение вещей.

Ну так у тебя тип ковариантный, вот и вся недолга...

КД>Возможно. Я просто воспроизвел хоровод классов из реального проекта.

Ну чем проще пример, тем проще понять, даже и самом в том числе

КД>Удивительно, что я вообще это заметил. После перехода на VC я конкретно расслабился в отношении таких вещей. Но, видать, сапёр во мне еще не умер

Дык это совершенно стандартное поведение. Если бы ты хотя бы указатель на встроенный тип возращал, что ли, а так всё просто тупо по станадарту...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: VC11. Фигня с виртуальными функциями.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 25.02.13 10:02
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Что-то я немного недоумеваю со следующего кода, который без проблем скомпилировался в VS2010 и VS2012.


Вот, кстати, попробовал наоборот — добавить const
////////////////////////////////////////////////////////////////////////////////
//class TAccessor

class TAccessor
{
 public:
  virtual TCheck* get_check()const=0;
};//class TCheck

////////////////////////////////////////////////////////////////////////////////
//class TObject

class TObject:public TAccessor
{
 public:
  virtual const TCheck* get_check()const override;
};//class TObject

Получил, как и положен, по морде

Ошибка 1 error C2555: TObject::get_check: возвращаемый тип перегруженной виртуальной функции отличается от "TAccessor::get_check" и не является ковариантным D:\Users\Dima\Work\TestCode\compiler_test\test_015\source\main.cpp 26
Ошибка 2 error C2556: TCheck *TObject::get_check(void) const: перегруженная функция отличается от "const TCheck *TObject::get_check(void) const" только возвращаемым типом D:\Users\Dima\Work\TestCode\compiler_test\test_015\source\main.cpp 30
Ошибка 3 error C2373: TObject::get_check: переопределение; различные модификаторы типа D:\Users\Dima\Work\TestCode\compiler_test\test_015\source\main.cpp 30

Вообщем, я думаю, компилятор и в первом случае должен был возбудиться
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: VC11. Фигня с виртуальными функциями.
От: Erop Россия  
Дата: 25.02.13 10:02
Оценка:
Здравствуйте, rg45, Вы писали:

R>Штука эта называется ковариантностью возвращаемых типов. В стандарте (как в старом, так и в новом) она прописана в п.10.3 и разрешена только для классов:


Точно! +100500, никогда не мог запомнить это бредовое название.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: VC11. Фигня с виртуальными функциями.
От: rg45 СССР  
Дата: 25.02.13 10:04
Оценка: +1
Здравствуйте, Коваленко Дмитрий, Вы писали:

E>>только можно попроще код-то замутить...

E>>
struct Base { virtual const Base* This() { return this; } };
E>>struct Der : Base { Base* This() override { return this; } } testObj;


КД>Возможно. Я просто воспроизвел хоровод классов из реального проекта.


КД>Удивительно, что я вообще это заметил. После перехода на VC я конкретно расслабился в отношении таких вещей. Но, видать, сапёр во мне еще не умер


А что, как по мне, это достаточно разумное и удобное решение. Типовая безопасность от этого не страдает — при обращении к объекту через указатель или ссылку на базу константность не снимается, а наоборот, добавляется. А вот если попытаться сделать наоборот (перенести модификатор const из базового класса в производный), тогда уже возникнет угроза непреднамеренного снятия константности, и в этом случае компилятор уже должен выдать ошибку.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: VC11. Фигня с виртуальными функциями.
От: Erop Россия  
Дата: 25.02.13 10:09
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Вообщем, я думаю, компилятор и в первом случае должен был возбудиться


Нет, не должен!
В ковариантности возвращаемого значения виртуальной функции то, что результат наследника должен приводиться к результату предка -- ключевое.
Код, который работает с указателем на прпедка должен же как-то мочь работать?..
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: VC11. Фигня с виртуальными функциями.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 25.02.13 10:11
Оценка: +1
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Вообщем, я думаю, компилятор и в первом случае должен был возбудиться


Хотя, если принять во внимание, что указателю на "неконстантное" можно присваивать указатель на "константное", а наоборот нельзя — то получается, что компилятор ведет себя вполне логично.

Ладно, век живи век учись.

Всем спасибо за проявленный интерес
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: VC11. Фигня с виртуальными функциями.
От: Mr.Delphist  
Дата: 25.02.13 10:43
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>>Вообщем, я думаю, компилятор и в первом случае должен был возбудиться


КД>Хотя, если принять во внимание, что указателю на "неконстантное" можно присваивать указатель на "константное", а наоборот нельзя — то получается, что компилятор ведет себя вполне логично.


Я бы на его месте таки бросал ворнинг...
Re[4]: VC11. Фигня с виртуальными функциями.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 25.02.13 10:54
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

КД>>>Вообщем, я думаю, компилятор и в первом случае должен был возбудиться


КД>>Хотя, если принять во внимание, что указателю на "неконстантное" можно присваивать указатель на "константное", а наоборот нельзя — то получается, что компилятор ведет себя вполне логично.


MD>Я бы на его месте таки бросал ворнинг...


Да не. С точки зрения компилятора — здесь (действительно) все чисто.

Я (сейчас) даже не уверен, что такое имеет смысл отслеживать на уровне статического (или как он там называется) анализа кода...
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[4]: VC11. Фигня с виртуальными функциями.
От: Evgeny.Panasyuk Россия  
Дата: 25.02.13 11:26
Оценка: :)
Здравствуйте, Erop, Вы писали:

R>>Штука эта называется ковариантностью возвращаемых типов. В стандарте (как в старом, так и в новом) она прописана в п.10.3 и разрешена только для классов:

E>Точно! +100500, никогда не мог запомнить это бредовое название.

А ещё есть контравариантность
Re[2]: VC11. Фигня с виртуальными функциями.
От: Logot Украина  
Дата: 25.02.13 15:54
Оценка:
Здравствуйте, Erop, Вы писали:

E>но тут есть такая штука, как ковариантность виртуальных функций, что ли, оно называется.


E>Ты можешь сделать так примерно:
struct Base {
E>    virtual ~Base() {}

E>    Base* Clone() const { return new Base( *this ); }
E>};

E>struct Der : Base {
E>    Der* Clone() const { return new Der( *this ); }
E>};
E>
и всё при этом получится, потому, что С++ позволяет в виртуальном методе наследника заменять тип результата на совместимый...



да, но если захочется сделать так:

struct Base
{
    virtual ~Base() {}

    std::tr1::shared_ptr<Base> Clone() const { return std::tr1::shared_ptr<Base>(new Base( *this )); }
};

struct Der : Base
{
    std::tr1::shared_ptr<Der> Clone() const { return std::tr1::shared_ptr<Der>(new Der( *this )); }
}


то облом
Re[3]: VC11. Фигня с виртуальными функциями.
От: Erop Россия  
Дата: 25.02.13 16:02
Оценка:
Здравствуйте, Logot, Вы писали:

L>да, но если захочется сделать так:


L>
L>struct Base
L>{
L>    virtual ~Base() {}

L>    std::tr1::shared_ptr<Base> Clone() const { return std::tr1::shared_ptr<Base>(new Base( *this )); }
L>};

L>struct Der : Base
L>{
L>    std::tr1::shared_ptr<Der> Clone() const { return std::tr1::shared_ptr<Der>(new Der( *this )); }
L>}
L>


L>то облом


Конечно облом. Как ты себе представляешь реализацию такой виртуальности?..
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: VC11. Фигня с виртуальными функциями.
От: Logot Украина  
Дата: 25.02.13 16:15
Оценка: :)
Здравствуйте, Erop, Вы писали:


E>Конечно облом. Как ты себе представляешь реализацию такой виртуальности?..


Не, ну почему нет?

имея
typedef base* base_ptr;
typedef der* der_ptr;


с условием, что везде используются base_ptr или dev_ptr, вместо base* или dev*

а потом как-то поменять на
typedef std::tr1::shared_ptr<base> base_ptr;
typedef std::tr1::shared_ptr<der> der_ptr;


очень хотелось бы, чтобы оно собралось в этом случае
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.