Виртуальный метод нельзя удалить в С++ 0x?

Сообщение об ошибке, кажется, немного вводит в заблуждение в сценарии, когда мы пытаемся delete использовать virtual метод.

prog.cpp:4:16: error: deleted function 'virtual void Test::foo()'
prog.cpp:8:2: error: used here

Код

struct Test : public Base
{
  Test() {}
  virtual void foo () = delete;  // error
};

Метод virtual не может быть delete по той же причине, почему они не могут оставаться нереализованными в C++03? Есть ли способ упомянуть, что Test намеренно не реализует virtual foo() ?


person iammilind    schedule 24.06.2011    source источник
comment
Попробуйте просто указать их как виртуальные без удаления.   -  person Yet Another Geek    schedule 24.06.2011
comment
Вау, я до сих пор никогда не осознавал, что ты можешь это сделать. И, во-вторых, это каким-либо образом полезно или полезно?   -  person DumbCoder    schedule 24.06.2011
comment
@DumbCoder: Ну, ты не можешь. Вы получаете ошибку. Отсюда этот вопрос.   -  person Lightness Races in Orbit    schedule 24.06.2011
comment
Если нам должно быть разрешено вызывать (Base*)->foo(), а Base* может быть Test*, то как мы можем позволить Test не реализовывать foo и не быть абстрактным? Какой смысл удалять функцию из производного класса, которая должна существовать?   -  person Kerrek SB    schedule 24.06.2011
comment
@Tomalak Geret'kal - меня больше интересовало, насколько это полезно и где бы вы его использовали? Надо было сделать это явным. Наверное, пойдет и посмотрит стандартный документ.   -  person DumbCoder    schedule 24.06.2011
comment
Это не имеет никакого смысла. Публичное наследование означает is-a, это фундаментальное требование, чтобы производный класс реализовывал (возможно, переопределял) полный виртуальный интерфейс.   -  person Gene Bushuyev    schedule 24.06.2011


Ответы (5)


Термин use имеет конкретное определение в стандарте, и, в частности, для виртуальных функций определение odr-used таково:

§3.2/2 (C++0x FDIS) [...] Виртуальная функция-член используется odr, если она не является чистой.[...]

Где odr-used — новый термин в будущем стандарте, который относится к тому, что в предыдущем стандарте называлось просто used:

§3.2/2 (текущий стандарт) [...] Виртуальная функция-член используется, если она не является чистой.[...]

Я считаю, что в сообщении об ошибке используется термин используется для ссылки на odr-used в данном конкретном случае, и да, причина, по которой это является нарушением, точно такая же. причина, по которой вы не можете оставить нечистую виртуальную функцию-член нереализованной.

person David Rodríguez - dribeas    schedule 24.06.2011
comment
Тогда имеет ли смысл указывать как delete, так и virtual для одной и той же функции? Мне кажется, что должен победить любой из них, и delete выигрывать не вижу смысла... - person Matthieu M.; 24.06.2011
comment
@Matthieu M.: Я предполагаю, что в стандарте может быть явно указано, что delete нельзя использовать с методом virtual, но опять же, он говорит так во всем наборе правил... Компилятор должен быть в состоянии предупредить вас, что эта конкретная комбинация никогда не будет работать, и, вероятно, в будущем они будут работать, на данный момент вы получаете слегка загадочную ошибку - person David Rodríguez - dribeas; 24.06.2011
comment
Я проверю это на CLang, надеюсь, диагностика будет более полезной. - person Matthieu M.; 24.06.2011

Все нечистые виртуальные функции должны быть реализованы, используете вы их или нет:

struct Test
{
  Test() {}
  virtual void foo();
};

int main() {
   Test* t = new Test;
   // ^ it seems to have to be dynamic allocation to coerce the error out
}

/* Output:
/home/Y3oGMf/ccOLuYWf.o: In function `main':
prog.cpp:(.text+0x17): undefined reference to `vtable for Test'
collect2: ld returned 1 exit status
*/

Я думаю, вы видите немного странное сообщение об ошибке, касающееся использования удаленной функции по аналогичной причине. Это просто из-за того, как все работает внутри. Более разумное сообщение об ошибке может быть в строке, где вы пытались delete использовать член функции virtual, говоря: «Это не сработает. Это вызовет проблемы, потому что эта реализация должна существовать».

В любом случае delete функция интерфейса не имеет смысла. Наследование добавляет функциональность; это не отнимает. Обратите внимание, что создание функции-члена чистой виртуальной запрещает создание экземпляра всего класса: производные классы должны повторно реализовать функциональность, чтобы она не была потеряна.

person Lightness Races in Orbit    schedule 24.06.2011
comment
У вас есть еще один угловой случай. Это ошибка компилятора... но, к сожалению, это страшная вещь. Проблема заключается в следующем: в какой объектный файл следует помещать vtable и typeinfo? C++ не требует единого файла .cpp для реализации всего класса. gcc принимает правило, что vtable и typeinfo создаются в том же объектном файле, что и первая не встроенная функция класса (здесь foo, поскольку конструктор является встроенным), и неспособность определить эту функцию приводит к этой странной ошибке компоновщика. . См. ideone.com/rEp41 (просто добавьте еще один невстроенный метод перед foo). - person Matthieu M.; 24.06.2011
comment
@Matthieu: я не понимаю, почему это угловой случай или ошибка компилятора. Обоснование, которое вы написали, кажется мне вполне правдоподобным и нормальным. - person Lightness Races in Orbit; 24.06.2011
comment
стандарт не указывает, когда выдавать информацию, мы могли бы утверждать, что соответствующий компилятор должен выдавать ее всегда, независимо от наличия других ошибок. Сообщение об ошибке, безусловно, сбивает с толку в первый раз, когда вы его видите! Но с точки зрения Стандарта программа теряет смысл при первой же ошибке, так что дискуссия неактуальна. - person Matthieu M.; 24.06.2011

Насколько я понимаю, ключевое слово delete предназначено для удаления реализации по умолчанию, созданной компилятором. Для виртуальных методов нет реализации по умолчанию, сгенерированной компилятором.

person Muhammad Hasan Khan    schedule 24.06.2011
comment
Для реализации по умолчанию у нас есть ключевое слово = default. = delete — запретить ссылаться на участника. - person iammilind; 24.06.2011
comment
delete можно использовать для удаления реализации по умолчанию и для запрета определенных членов (что на самом деле одно и то же). - person R. Martinho Fernandes; 24.06.2011
comment
@iammilind: Действительно, delete запрещает что-то. Но вы не можете запретить в производном классе что-то, что базовый класс обещает существовать во всех своих производных классах, не так ли? - person Kerrek SB; 24.06.2011

Разрешены удаленные виртуальные функции, но не переопределение их.

§10.3(11) говорит:

Виртуальная функция, объявленная в классе, должна быть определена или объявлена ​​чистой (10.4) в этом классе, или и то, и другое; но диагностика не требуется (3.2).

В §8.4.1 говорится, что удаленная функция определена.

И §10.3(16) говорит:

Функция с удаленным определением (8.4) не должна переопределять функцию, не имеющую удаленного определения. Точно так же функция, не имеющая удаленного определения, не должна переопределять функцию с удаленным определением.

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

Таким образом, следующий код неправильно сформирован:

struct Base {
    virtual void foo();
};
struct Derived : public Base {
    virtual void foo() = delete;
};

Но следующий код правильно сформирован:

struct Base {
    virtual void foo() = delete;
};
struct Derived : public Base {
    virtual void foo() = delete;
};

ДЕМО

У OP была либо ошибка компилятора, либо проблема QoI (трудно сказать, что именно, потому что он не опубликовал весь код), которая с тех пор была исправлена.

person Oktalist    schedule 27.09.2014

Возможно, вам не следует наследовать от класса, который ТРЕБУЕТ что-то, если ваш производный класс не может это реализовать. Вероятно, вам не нужно наследовать от этого класса в первую очередь.

Это все равно, что сказать: «Я делаю лучшую машину. Это своего рода машина, но без колес». Вы уверены, что это машина?

Или ты спрашиваешь это просто из любопытства?

person RedX    schedule 24.06.2011
comment
Это должен был быть комментарий? - person Lightness Races in Orbit; 24.06.2011