Почему классы шаблонов допускают функции, которые невозможно скомпилировать?
class P
{
};
template< typename P >
class C : public P
{
public:
void f()
{
P::f();
}
};
int main() {
C<P> c1;
return 0;
}
На всякий случай мой вопрос оставляет место для недопонимания, это пример кода. Если C
не был шаблоном, а унаследован от P
напрямую, то образец не смог бы скомпилироваться, потому что явно функция f()
пытается вызвать функцию в базовом классе P
, которая не существует.
Однако, если C
является шаблоном, тогда это происходит, только если f()
фактически вызывается.
Я хотел бы знать, почему существует такая разница. В обоих случаях f()
был бы мертвым кодом и все равно лишен, однако программа неформатирована в сценарии без шаблонов.
Ответы
Ответ 1
Собственно, программа, которую вы опубликовали, не плохо сформирована. Хотя "неявное создание экземпляра" C<P> c1;
создает экземпляры всех объявлений участников,
специализация члена неявно создается, когда специализация ссылается в контексте, который требует определения члена
[N4140, 14.7.1 (2)] Поскольку вы никогда не используете C<P>::f
, его определение никогда не требуется и, следовательно, никогда не создается.
Это отличается от "явного экземпляра", например
template class C<P>;
который должен был бы определить определение всех членов C<P>
и, таким образом, привести к ошибке.
Ответ 2
Потому что имеет смысл разрешить использовать только те функции, которые действительно хорошо сформированы.
Возьмите vector::push_back(const T&)
, для которого требуется T
копировать. Но вы все равно можете использовать большую часть vector
, даже если T
является подвижным не скопированным типом.
Ответ 3
Внутри класса шаблона идентификатор P
является параметром типа шаблона, он не относится к class P
, определенному ранее. Поэтому вы не можете сказать, что P::f()
не существует, так как вы не знаете, какой класс будет заменен параметром P
.
И позже, когда вы вызываете шаблон для объявления переменной c1
, функция не нужна, поэтому нет "мертвого кода" - есть только шаблон, который никогда не был расширен до фактического кода.
Ответ 4
Если я пишу класс без шаблона, который наследует от P
, вызов несуществующей функции в этом классе из функции-члена, очевидно, является ошибкой, поскольку для этого нет никакой пользы.
Если C
является классом шаблона, возможно, этот ошибочный вызов действителен для другого P
. Возможно, эта функция-член представляет собой расширенную функциональность для типов, поддерживающих определенный интерфейс. C
может быть создан из некоторой внешней библиотеки, о которой я не знаю при создании экземпляра для P
. Таким образом, бросание ошибки компилятора для неиспользуемой функции в классе шаблона, которое может быть действительным для определенных типов, ограничивало бы выразительную силу шаблонов. Он также уменьшает время компиляции, двоичные размеры и т.д., Потому что ненужную функцию шаблона не нужно генерировать для типов, если она не используется.
Ответ 5
Переменная C<P> c1;
не используется, а функция C::f()
никогда не вызывается. Компилятор не создает тело функции до его использования. Это простая оптимизация, которая ускоряет компиляцию.