Виртуальная функция-член используется, если она не является чистой? - PullRequest
8 голосов
/ 10 ноября 2010

C++03 3.2.2 ... Объект или не перегруженная функция используется, если ее имя появляется в потенциально оцененном выражении.Виртуальная функция-член используется, если она не является чистой ...

А затем в 3.2.3 мы имеем: Каждая программа должна содержать ровно одно определениекаждая не встроенная функция или объект, который используется в этой программе;Диагностика не требуется.Определение может явным образом появиться в программе, оно может быть найдено в стандартной или пользовательской библиотеке или (при необходимости) неявно определено (см. 12.1, 12.4 и 12.8).Встроенная функция должна быть определена в каждой единице перевода, в которой она используется.

По строкам, которые я читаю: чисто виртуальная функция не используется.ODR применяется только к тем функциям, которые используются.Не означает ли это, что следующее будет законным?Я предполагаю, что ответ - нет, это не так, но тогда я не могу понять, почему.

//x.h
struct A
{
   virtual void f() = 0;
};

//y.cpp
#include "x.h"
void A::f()
{
}

//z.cpp
#include "x.h"
#include <iostream>
void A::f()
{
   std::cout << "Hello" << std::endl;
}

//main.cpp
#include "x.h"
struct B:A
{
   virtual void f()
   {
      A::f();
   }
};

int main()
{
   A* p = new B;
   p->f();
}

Ответы [ 5 ]

11 голосов
/ 10 ноября 2010

Два пункта не являются взаимоисключающими. То, что виртуальная функция используется, если она не является чистой, не означает, что верно обратное. Если виртуальная функция является чистой, это не значит, что она обязательно не используется. Он по-прежнему может использоваться «если его имя появляется в потенциально оцененном выражении», например в вашем примере:

3 голосов
/ 10 ноября 2010

Этот код нарушает ODR. A :: f многократно определен. Следовательно, у него есть UB.

Множественные определения в единицах перевода допускаются только для следующих по $ 3.2 / 5

Может быть более одного определения типа класса (пункт 9), тип перечисления (7.2), встроенный функция с внешней связью (7.1.2), шаблон класса (пункт 14), шаблон нестатической функции (14.5.5), статический член данных шаблона класса (14.5.1.3), функция-член класса шаблон (14.5.1.1) или шаблон специализация для которой какой-то шаблон параметры не указаны (14.7, 14.5.4) в программе при условии, что каждое определение появляется в другом переводчик и предоставил определения удовлетворяют следующему требования.

1 голос
/ 28 ноября 2010

Это связано, но не по теме: из цитат кажется, что в Стандарте есть дыра, хорошо: в нем также должно быть сказано, что используется чистый виртуальный деструктор, и что он должен быть определен;по крайней мере, если существуют какие-либо объекты производного класса, которые уничтожаются, или если деструктор такого класса определен, поскольку деструктор производного класса должен вызывать деструктор базового класса, неявно это происходит с помощью синтаксиса квалифицированного :: id.Определение таких деструкторов обычно тривиально, но не может быть исключено и не может быть сгенерировано.

1 голос
/ 10 ноября 2010

Как отметил @Charles Bailey, ваш A::f фактически используется, даже если он чисто виртуальный. Но это не главное.

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

3.2p1 Ни одна единица перевода не должна содержать более одного определения любой переменной, функции, типа класса, типа перечисления или шаблона.

3.2p3 Каждая программа должна содержать ровно одно определение каждой не встроенной функции или объекта, которая используется в этой программе; Диагностика не требуется.

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

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

// x.cpp
void f() {}
void g() {}

// y.cpp
#include <iostream>
void f() {
  std::cout << "Huh" << std::endl;
}
void h() {}

// z.cpp
void g();
void h();
int main() { 
  g();
  h();
  return 0;
}
0 голосов
/ 10 ноября 2010

[class.abstract]: «Чистая виртуальная функция должна быть определена только в том случае, если она вызывается с помощью или, как если бы с (12.4), синтаксис квалифицированного идентификатора (5.1)."

Ваш A::fвызывается B::f, поэтому должно быть одно определение A::f.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...