Russian Qt Forum

Компиляторы и платформы => Компиляторы => Тема начата: Racheengel от Июль 13, 2017, 16:20



Название: Возможный баг в компиляторе MSVC 2013
Отправлено: Racheengel от Июль 13, 2017, 16:20
Всем привет...
Наткнулся на странную вещь, которую иначе как багом компилятора, объяснить трудно.

Есть 2 интерфейса, IName и ITaskName. Оба имеют одинаковый метод GetName:

Цитировать
class IName{
public:
virtual QString GetName() const = 0;
}

class ITaskName{
public:
virtual QString GetName() const = 0;
}

Для IName существует имплементация CName:

Код:
class CName: virtual public IName{
public:
virtual QString GetName() const { return m_name; }
}

И есть имплементация для ITaskName, которая отнаследована также от CName:

Код:
class CTask: virtual public CName, virtual public ITaskName
{
public:
using CName::GetName;
}

Вроде бы все ок - проблема неоднозначности родителя решается через виртуальное наследование + конкретное указание наследованного метода через using . Однако компилятор упорно сообщает, что не может создать абстракный класс CTask.

Решается эта проблема следующим костылем:

Код:
class CTask
...
virtual const QString GetName() const { return CName::GetName(); }
...

но в таком случае возникает вопрос - а что не так с using CName::GetName;  ???


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: __Heaven__ от Июль 13, 2017, 17:03
Решается эта проблема следующим костылем:

Код:
class CTask
...
virtual const QString GetName() const { return CName::GetName(); }
...
Мне кажется, что таким образом перегружается метод ITaskName::GetName, который в интерфейсе объявлен чистым виртуальным. Без объявления экземпляров класса CTask, вроде, должно всё скомпилироваться, а сам CTask будет являться абстрактным классом.


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: Авварон от Июль 13, 2017, 17:31
В гцц та же проблема. АФАИК, это не имеет ничего общего с виртуальным наследованием - оно призвано решать проблему ромбовидного наследования (а точнее, когда один и тот же базовый класс встречается в двух ветвях иерархии). Здесь же ситуация другая - есть разные классы (но с одноименными методами).


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: __Heaven__ от Июль 13, 2017, 17:44
В gcc нет такой проблемы. пруф (https://ideone.com/yOImk6)


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: Авварон от Июль 13, 2017, 17:55
В 4.8.4 есть:
Цитировать
/usr/bin/g++ --version
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4

/usr/bin/g++ -g -O0 -Wall -Wextra -m64 -pipe -fexceptions -fvisibility=default -fPIC -DQT_DEPRECATED_WARNINGS -DQT_CORE_LIB -I/home/abbapoh/Qt/5.9/gcc_64/include -I/home/abbapoh/Qt/5.9/gcc_64/include/QtCore -I/home/abbapoh/Qt/5.9/gcc_64/mkspecs/linux-g++ -I/home/abbapoh/programming/build-cpptest-Desktop_Qt_5_9_0_GCC_64bit-Debug/qtc_Desktop__baa544a4-debug/cpptest.qtc-Desktop--baa544a4.063e55c3/qt.headers -std=c++0x -o /home/abbapoh/programming/build-cpptest-Desktop_Qt_5_9_0_GCC_64bit-Debug/qtc_Desktop__baa544a4-debug/cpptest.qtc-Desktop--baa544a4.063e55c3/.obj/3a52ce780950d4d9/main.cpp.o -c /home/abbapoh/programming/cpptest/main.cpp
/home/abbapoh/programming/cpptest/main.cpp: In function ‘void g()’:
/home/abbapoh/programming/cpptest/main.cpp:43:8: warning: unused variable ‘dr’ [-Wunused-variable]
     D& dr = dynamic_cast<D&>(*bp); // fails
        ^
/home/abbapoh/programming/cpptest/main.cpp: In function ‘int main(int, char**)’:
/home/abbapoh/programming/cpptest/main.cpp:75:11: error: cannot declare variable ‘t’ to be of abstract type ‘CTask’
     CTask t;
           ^
/home/abbapoh/programming/cpptest/main.cpp:65:7: note:   because the following virtual functions are pure within ‘CTask’:
 class CTask: virtual public CName, virtual public ITaskName
       ^
/home/abbapoh/programming/cpptest/main.cpp:57:17: note:    virtual QString ITaskName::GetName() const
 virtual QString GetName() const = 0;
                 ^


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: Авварон от Июль 13, 2017, 18:01
В gcc нет такой проблемы. пруф (https://ideone.com/yOImk6)

Лол, ну вы бы хоть инстанс класса бы создали :D

Проверил - gcc, clang и icc не хавают. Ни одна из версий.


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: __Heaven__ от Июль 13, 2017, 18:04
Эта диагностика показывает о попытке создать экземпляр/разыменовать указатель на абстрактный класс


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: __Heaven__ от Июль 13, 2017, 18:05
Более простым для восприятия кодом
Код
C++ (Qt)
#include <iostream>
 
class IPrint {
public:
virtual void print() = 0;
};
 
class Print: public IPrint {
};
 
 
int main() {
IPrint t;
return 0;
}

Цитировать
prog.cpp: In function ‘int main()’:
prog.cpp:13:9: error: cannot declare variable ‘t’ to be of abstract type ‘IPrint’
  IPrint t;
         ^
prog.cpp:3:7: note:   because the following virtual functions are pure within ‘IPrint’:
 class IPrint {
       ^~~~~~
prog.cpp:5:15: note:    virtual void IPrint::print()
  virtual void print() = 0;
               ^~~~~


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: Авварон от Июль 13, 2017, 18:08
Ииии? У топикстартера именно эта проблема и есть:
Цитировать
Однако компилятор упорно сообщает, что не может создать абстракный класс CTask.


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: __Heaven__ от Июль 13, 2017, 18:11
В gcc нет такой проблемы. пруф (https://ideone.com/yOImk6)
Класс создан (объявлен). Создать экземпляр абстрактного класса невозможно в соответствии со стандартом c++.


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: Авварон от Июль 13, 2017, 18:19
Рукалицо.


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: __Heaven__ от Июль 13, 2017, 18:20
Что не так?


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: Igors от Июль 13, 2017, 18:22
Не понял
Код:
class CTask: virtual public CName, virtual public ITaskName
{
public:
using CName::GetName;
}
ITaskName базовый, имеет чистый виртуал, т.е. этот слот в VMT должен быть заполнен. С какой стати туда должно подставляться что-то автоматом?  Ну и вообще, как-то "нездорово"


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: Racheengel от Июль 13, 2017, 19:09
ITaskName это интерфейс, "чисто абстрактный" класс.
Поэтому автоматом ничего и не создается - он служит только для предоставления доступа к перегруженным методам имплементаций.

А CTask - это конкретная имплементация, которая наследована от базового CName, имеющего тот же метод, что и ITaskName, на уровне объявления интерфейса.

Я так понимаю, что проблема в том, что базовые классы ITaskName и IName не имеют общего предка, объявляющего метод GetName. Но разве это не должно разруливаться на этапе линковки, т.к. оба метода на уровне интерфейсов - абстрактны? Тем более CName уже помещает в виртуальную таблицу свой CName::GetName, не понятно, почему линкер не может связать ITaskName::GetName с CName::GetName по одинаковым сигнатурам.



Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: __Heaven__ от Июль 13, 2017, 21:22
Racheengel, дело в том, что CTask содержит не одну виртуальную таблицу, а две, в каждой из которой имеется адрес на метод GetName и адреса напротив них записываются разные.


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: Igors от Июль 14, 2017, 09:34
не понятно, почему линкер не может связать ITaskName::GetName с CName::GetName по одинаковым сигнатурам.
Линкер тут ни при чем. Допустим Вы написали
Код
C++ (Qt)
CTask * task;
...
ITaskName * test = task;
 
Ожидается что test - наследник абстрактного ITaskName, т.е. поведение его виртуальных методов не зависит от раскладов множ наследования. А иначе выходит что подключая один и тот же класс туда-сюда мы изменяем его методы - так низзя


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: Racheengel от Июль 14, 2017, 10:31
Racheengel, дело в том, что CTask содержит не одну виртуальную таблицу, а две, в каждой из которой имеется адрес на метод GetName и адреса напротив них записываются разные.

Вижу, но вот это как раз и кажется странным - почему две то?
Сигнатуры методов одинаковые, они оба абстракные, помечены как virtual по всему пути наследования.
Я бы ожидал, что абстракный ITaskName::GetName будет помещен в VMT от CTask.


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: Igors от Июль 14, 2017, 11:50
Вижу, но вот это как раз и кажется странным - почему две то?
Потому что наследник автоматически приводится к любому базовому типу, который не может иметь "посторонок" в VMT


Название: Re: Возможный баг в компиляторе MSVC 2013
Отправлено: _Bers от Август 18, 2017, 08:24
Всем привет...
Наткнулся на странную вещь, которую иначе как багом компилятора, объяснить трудно.

вы отнаследовались от интерфейса.
а реализации чисто виртуальной функции не предоставили.

и почему то это у вас наследника нельзя создать?

и на будущие:
вместо того, что бы через одно место объяснять на пальцах,
лучше привести код (на онлайн компиляторе),
который наглядно иллюстрирует проблему.

как это сделали моральные люди в этой самой теме.