Конфликтующие атрибуты типа, указанные для виртуального деструктора

Следующий отрывок ранее компилировался под Borland C++, MSVC и OpenWatcom:

class aaa {
    virtual _fastcall ~aaa();
};

class bbb:public aaa {
};

Он не компилируется под gcc/g++ (MinGW 4.8.0). Ошибка:

probz.cpp:7:7: error: conflicting type attributes specified for 'virtual bbb::~bbb()'
 class bbb:public aaa {
       ^
probz.cpp:3:20: error:   overriding 'virtual aaa::~aaa()'
   virtual _fastcall ~aaa()=0;///can't be abstract
                     ^

Очевидно, что bbb::~bbb() не существует!

РЕДАКТИРОВАТЬ

Фактическая иерархия классов больше, есть много классов bbb, наследуемых от aaa, и между ними есть промежуточные члены, например, bbb расширяет abb, который расширяет aab, который расширяет aaa. . aaa действительно имеет абстрактный виртуальный деструктор, который получает реализацию в промежуточных классах, но не в листьях. Да, я могу удалить атрибут __fastcall, и он скомпилируется. Это ограничение gcc, что я не могу настроить соглашение о вызовах?


person MKaama    schedule 07.02.2015    source источник
comment
У вас может быть чистый виртуальный деструктор, но вам все равно нужно предоставить определение, иначе как любой производный класс уничтожит свой базовый класс? Добавьте inline aaa::~aaa() {} под определением class aaa.   -  person Praetorian    schedule 07.02.2015
comment
Как вы думаете, почему нет bbb::~bbb()? Хотя при подкладке должно быть найдено определение для чисто-виртуальной базы-дтор.   -  person Deduplicator    schedule 07.02.2015
comment
Абстрактность не является ни причиной, ни сутью вопроса. Измените деструктор на ‹code›виртуальный _fastcall ~aaa() {/*I am not abstract*/};‹/code›, и у вас все еще будет та же ошибка. Это взято из работающей и полезной библиотеки, которую я пытаюсь портировать.   -  person MKaama    schedule 07.02.2015
comment
@MKaama Тогда попробуйте удалить _fastcall, возможно, gcc (MinGW) не знает, что это значит. Я снова открою вопрос, но, пожалуйста, избавьтесь от чистого виртуального деструктора или добавьте для него определение.   -  person Praetorian    schedule 07.02.2015
comment
@Praetorian THX за указание, я исправил этот ответ.   -  person πάντα ῥεῖ    schedule 07.02.2015
comment
Кажется, что gcc принял атрибут __fastcall для деструктора базового класса и жалуется на то, что он не указан в производном классе. В этом есть смысл — вам нужно согласованное соглашение о вызовах для всех реализаций одной и той же виртуальной функции, иначе вы не сможете отправлять их полиморфно. Возможно, эти другие компиляторы были готовы позволить это подразумевать, но GCC нет - такие спецификаторы находятся за пределами стандарта языка C++, поэтому компиляторы могут свободно принимать все, что они считают разумным.   -  person Tony Delroy    schedule 07.02.2015
comment
@TonyD Звучит логично, но чтобы указать __fastcall в производном классе, мне пришлось бы переопределить деструктор, чего я не хочу делать.   -  person MKaama    schedule 07.02.2015
comment
@MKaama, тогда предложение Praetorian удалить его из базового класса звучит как разумная альтернатива ... чем меньше нестандартного дерьма в вашем коде, тем лучше, ИМХО.   -  person Tony Delroy    schedule 07.02.2015


Ответы (1)


__fastcall — это соглашение о вызовах.

Это нестандартная функция: двойное подчеркивание в начале имени означает, что она зависит от реализации. Соглашения о вызовах тесно связаны с системами и архитектурами ЦП. Это похоже на 32-битный режим x86.

Некоторые рекомендации:

  • Не следует заморачиваться с соглашениями о вызовах, за исключением случаев крайней необходимости (например, при взаимодействии с предварительно скомпилированными библиотеками или внешние библиотеки).
  • Уровень оптимизации, который выполняют современные компиляторы, больше не требует такой ручной настройки. То же самое относится и к ключевому слову register
  • Если ваш код действительно нуждается в этом на некоторых платформах, вы должны предусмотреть #define в одном из ваших заголовков и убедиться с помощью условной компиляции, что он ничего не определяет на платформе/компиляторах, где это не имеет значения (для DLL этот подход является обычным, с библиотекой конкретный #define).
  • Соглашения о вызовах должны быть одинаковыми во всех производных классах, поэтому их лучше объявлять в базовом классе. Для виртуальных функций это неявно обязательное требование! Представьте, что ваш базовый класс передает параметры через стек, а производный класс — через регистры (fastcall). Какой же код должен сгенерировать ваш компилятор, если вы сделаете полиморфный вызов через указатель базового класса? А что произойдет, если два производных класса будут использовать разные соглашения о вызовах?
  • Вы не можете предположить, что соглашение о вызовах наследуется автоматически: стандарт не дает никаких гарантий, здесь
  • Если вам нужно указать соглашение о вызовах, по возможности предпочтите спецификатор внешней связи (например, extern "C" ), потому что это единственная семантика, относящаяся к соглашению о вызовах, которая поддерживается стандартом.

Дополнительная информация:

person Christophe    schedule 28.02.2015