Russian Qt Forum
Ноябрь 22, 2024, 14:53 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Возможный баг в компиляторе MSVC 2013  (Прочитано 19108 раз)
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« : Июль 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;  Непонимающий
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #1 : Июль 13, 2017, 17:03 »

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

Код:
class CTask
...
virtual const QString GetName() const { return CName::GetName(); }
...
Мне кажется, что таким образом перегружается метод ITaskName::GetName, который в интерфейсе объявлен чистым виртуальным. Без объявления экземпляров класса CTask, вроде, должно всё скомпилироваться, а сам CTask будет являться абстрактным классом.
« Последнее редактирование: Июль 13, 2017, 17:08 от __Heaven__ » Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #2 : Июль 13, 2017, 17:31 »

В гцц та же проблема. АФАИК, это не имеет ничего общего с виртуальным наследованием - оно призвано решать проблему ромбовидного наследования (а точнее, когда один и тот же базовый класс встречается в двух ветвях иерархии). Здесь же ситуация другая - есть разные классы (но с одноименными методами).
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #3 : Июль 13, 2017, 17:44 »

В gcc нет такой проблемы. пруф
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #4 : Июль 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;
                 ^
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #5 : Июль 13, 2017, 18:01 »

В gcc нет такой проблемы. пруф

Лол, ну вы бы хоть инстанс класса бы создали Веселый

Проверил - gcc, clang и icc не хавают. Ни одна из версий.
« Последнее редактирование: Июль 13, 2017, 18:03 от Авварон » Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #6 : Июль 13, 2017, 18:04 »

Эта диагностика показывает о попытке создать экземпляр/разыменовать указатель на абстрактный класс
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #7 : Июль 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;
               ^~~~~
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #8 : Июль 13, 2017, 18:08 »

Ииии? У топикстартера именно эта проблема и есть:
Цитировать
Однако компилятор упорно сообщает, что не может создать абстракный класс CTask.
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #9 : Июль 13, 2017, 18:11 »

В gcc нет такой проблемы. пруф
Класс создан (объявлен). Создать экземпляр абстрактного класса невозможно в соответствии со стандартом c++.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #10 : Июль 13, 2017, 18:19 »

Рукалицо.
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #11 : Июль 13, 2017, 18:20 »

Что не так?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Июль 13, 2017, 18:22 »

Не понял
Код:
class CTask: virtual public CName, virtual public ITaskName
{
public:
using CName::GetName;
}
ITaskName базовый, имеет чистый виртуал, т.е. этот слот в VMT должен быть заполнен. С какой стати туда должно подставляться что-то автоматом?  Ну и вообще, как-то "нездорово"
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #13 : Июль 13, 2017, 19:09 »

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

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

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

Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #14 : Июль 13, 2017, 21:22 »

Racheengel, дело в том, что CTask содержит не одну виртуальную таблицу, а две, в каждой из которой имеется адрес на метод GetName и адреса напротив них записываются разные.
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.131 секунд. Запросов: 23.