LINUX.ORG.RU

С++ ошибка компилятора в таком виде: баг или фича?

 ,


0

1
class A
{
 public:

    int data;
    virtual void method(){} //объявлена и определена
    virtual ~A(){}
};

class B : public A
{
public:
    virtual void method(); //объявлена, но ошибочно не определена
} b;

gcc 9.3.0

Вывод компилятора

/usr/bin/ld: test.o: in function `B::B()':
test.h:19: undefined reference to `vtable for B'
/usr/bin/ld: test.o: in function `B::~B()':
test.h:19: undefined reference to `vtable for B'

То есть, при отсутствии хотя бы одной виртуальной функции класса, компилятор «ругается» на отсутствие конструктора, деструктора, но НЕ самой отсутствующей функции

★★★★★

Ругается не на отсутствие конструктора/деструктора, а на отсутствие vtable, ссылка на который обнаружена в (неявно сгенерированных) конструкторе и деструкторе.

Так что фича.

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 3)

но НЕ самой отсутствующей функции

В MSVC на саму функцию ругается, других ошибок нет:

error LNK2001: неразрешенный внешний символ "public: virtual void __cdecl B::method(void)"
fsb4000 ★★★★★
()
Ответ на: комментарий от intelfx

И? Чем плохо посмотреть как в других реализациях? Можно и в clang глянуть и в других если у кого есть…

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

Чего мелочиться? В Си вот нет виртуальных функций, поэтому там такая ошибка невозможна. Зачем ограничиваться C++? Можно ещё посмотреть, как в Ruby дела обстоят.

i-rinat ★★★★★
()
Ответ на: комментарий от fsb4000

ну это хотя бы более читабельно

next_time ★★★★★
() автор топика
Ответ на: комментарий от next_time

а почему он этот самый vtable не создаёт

Осмелюсь предположить — потому что ты определил не все виртуальные функции.

intelfx ★★★★★
()
Ответ на: комментарий от intelfx

vtable — это таблица адресов всех виртуальных функций класса?

кто вам это сказал? согласно стандарту такой сущности не существует, компилятор имеет право хоть switch-ами виртуальные функции имплементировать

next_time ★★★★★
() автор топика
Ответ на: комментарий от next_time

А, лол, ты из этих...

кто вам это сказал?

РМС в ушко нашептал.

согласно стандарту такой сущности не существует, компилятор имеет право хоть switch-ами виртуальные функции имплементировать

И что?

intelfx ★★★★★
()

https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vague-vtable

The virtual table for a class is emitted in the same object containing the definition of its key function, i.e. the first non-pure virtual function that is not inline at the point of class definition. If there is no key function, it is emitted everywhere used. The emitted virtual table includes the full virtual table group for the class, any new construction virtual tables required for subobjects, and the VTT for the class. They are emitted in a COMDAT group, with the virtual table mangled name as the identifying symbol. Note that if the key function is not declared inline in the class definition, but its definition later is always declared inline, it will be emitted in every object containing the definition.

B::method() здесь является «key function» и генерация vtable привязана к нему. Это оптимизация: https://gcc.gnu.org/wiki/VerboseDiagnostics#missing_vtable

xaizek ★★★★★
()
Ответ на: комментарий от next_time

Кроме нарушения стандарта?

В чём состоит нарушение стандарта? Ты не определил тело функции, программа невалидна. Как именно компилятор тебя пошлёт в пешее эротическое, это уже дело компилятора.

Ох уж эти доморощенные буквоеды. Хотите буквоедствовать, хотя бы делайте это правильно.

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 1)
Ответ на: комментарий от next_time

Я тебе сказал, почему компилятор сообщает тебе именно эту ошибку (не совсем точно, но тем не менее). Это настолько далеко от буквоедства, насколько возможно. А ты дальше начал нести какую-то чушь про «согласно стандарту такой сущности не существует». Прикинь, согласно стандарту и самого GCC не существует, иди выкинь его :-)

intelfx ★★★★★
()
Ответ на: комментарий от intelfx

чушь про «согласно стандарту такой сущности не существует».

покажите, пожалуйста, где в стандарте есть что-то про vtable?

next_time ★★★★★
() автор топика
Ответ на: комментарий от intelfx

Я тебе сказал, почему компилятор сообщает тебе именно эту ошибку

в первом сообщении, которое, действительно, было по делу… а потом Остапа понесло

next_time ★★★★★
() автор топика
Ответ на: комментарий от next_time

Не «это чушь», а «ты начал нести чушь». Потому что это совершенно верное, но совершенно нерелевантное утверждение, которое никак не противоречит тому, что я написал, но ты попытался выдать его за контраргумент-возражение.

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 1)
Ответ на: комментарий от intelfx

В этом споре победит тот, кто раньше успокоится!

anonymous
()

19 это номер строки с ошибкой? ну и не придирайся

гы. Зажрался ты, братец.

/tmp/ccbr9YoM.o: In function `B::B()': test.cpp:(.text._ZN1BC2Ev[_ZN1BC5Ev]+0xf): undefined reference to `vtable for B' collect2: error: ld returned 1 exit status

ilovewindows ★★★★★
()
Последнее исправление: ilovewindows (всего исправлений: 1)
Ответ на: комментарий от ilovewindows

19 это номер строки с ошибкой?

не-а, это номер строки с объявлением класса

next_time ★★★★★
() автор топика
Ответ на: комментарий от intelfx

Вот вы написали также. Потому, что зачем компилятор создаёт таблицу виртуальных функций понятно. Понятно даже, зачем он пытается запихнуть в неё все виртуальные функции. Вопрос был в том, зачем он её удаляет, как только встречает undefined виртуальный метод. Можно же просто nullptr на место адреса неопределённого метода написать, например.

next_time ★★★★★
() автор топика
Ответ на: комментарий от next_time

Я не вижу, как ODR нарушался бы в этой ситуации. У тебя где-то метод определяется два раза?

Как вообще наличие vtable может нарушать ODR?

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от next_time

Вопрос был в том, зачем он её удаляет, как только встречает undefined виртуальный метод. Можно же просто nullptr на место адреса неопределённого метода написать, например.

Не удаляет, а не создаёт. А почему конкретно — @xaizek ответил выше ссылкой на документацию, более точного ответа дать нельзя.

intelfx ★★★★★
()

Читаю тред и думаю, что ОП мог бы быть и поумнее.

Выражаю моральную поддержку @intelfx в деле борьбы с тупняком.

Pavval ★★★★★
()

А ты не самый острый нож в наборе.

anonymous
()
Ответ на: комментарий от Siborgium

Чтобы дальше продолжить, метода нет, но он и нигде не вызывается. А так и ошибку не показывает и дальше не дает делать.

ilovewindows ★★★★★
()
Ответ на: комментарий от ilovewindows

Чтобы дальше продолжить, метода нет,

Метод есть, у него нет тела.

но он и нигде не вызывается

Это забота оптимизатора.

А так и ошибку не показывает и дальше не дает делать.

Потому что нет ошибки. ТС может написать реализацию B::method в другом файле и потом слинковать все это вместе. С т.з. компилятора все верно.

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

К пуговицам претензии есть, к пуговицам претензий нет. Давай по другому спрошу, исполняемый модуль собран? Вменяемое сообщение с номером строки или именем отсутствующего метода выдано?

ilovewindows ★★★★★
()
Ответ на: комментарий от next_time

было бы хреново, если бы компилятор позволял себе вставлять тело функции. есть некое тело функции, когда она чисто виртуальная, там вызывается terminate. сделано на случай, если кто-нибудь таки умудрится вызвать чисто виртуальную функцию. такой вызов - серьезная ошибка логики самой программы. также ошибка программиста - объявять функцию и не использовать ее.

а компилятор все правильно говорит. то, что в классе наследнике определена только часть требуемых виртуальных функций, а не все, ошибкой не является, ведь от него можно дальше продолжить иерархию и определить остальные. ошибка - это созднание объекта, где не все функции определены. поэтому он и говорит, что нет таблицы, на которую ссылаться, а не «ой, я не смог создать таблицу виртуальных функций».

кстати, компилятор обычно следующими сообщениями подсказывает какие функции следует определить, чтобы таблица таки появилась.

zerhud
()
Ответ на: комментарий от ilovewindows

Что к чему… Как тебе линковщик будет выдавать

Вменяемое сообщение с номером строки или именем отсутствующего метода

если он не оперирует исходными файлами?

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Классная позиция - компилятор не ругается потому что не его дело, линкер не ругается потому что нечем. Ругается линкер именами, конечно, но замнем для ясности. зы. Весь из себя коммерческий IAR тоже самое лепит про отсутствие vtbl. Кто-то у кого-то содрал.

ilovewindows ★★★★★
()
Ответ на: комментарий от ilovewindows

компилятор не ругается потому что не его дело

А что, его?

Вот, например, такой код. При (раздельной) компиляции каждого объектника одна из реализаций будет недоступна компилятору, тот же случай, что и у ТС. Тем не менее, если линковщику предоставить объектник, в котором реализация есть, программа успешно соберется. За вычетом мелочей типа инстанса B b это ровно тот случай, что у ТС.

// decls.hxx
class A
{
 public:

    int data;
    virtual void method();
    virtual ~A(){}
};

class B : public A
{
public:
    virtual void method();
};
// object1.cxx
#include "decls.hxx"

void A::method(){}
// object2.cxx
#include "decls.hxx"

void B::method(){}

линкер не ругается потому что нечем. Ругается линкер именами, конечно

Линковщик ругается заманглеными именами, потому что только они ему и доступны. Изучите процесс сборки типичной программы.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 2)
Ответ на: комментарий от Siborgium

Давай нетипичной программы, вот есть вполне конкретная ошибка - забыта реализация виртуального метода, в классе 100 тыщ методов. Что это баг, фича, вредительство мне глубоко по барабану. Как искать забытый? Сообщение из MSVC тебе привели, обхаянный всеми паскаль ткнет носом в ошибку на этапе компиляции.

ilovewindows ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.