Ответ 1
Вот вещь. В то время как для нас может показаться, что компилятор знает все, что ему нужно знать о типах, о которых идет речь, стандарт говорит иначе.
... [Примечание: Аргумент типа шаблона может быть неполным типом. - конечная нота]
Класс, который был объявлен, но не определен, тип перечисления в определенные контексты ([dcl.enum]) или массив неизвестной границы или тип неполного элемента - это не полностью определенный тип объекта.46 Неполно определенные типы объектов и cv void являются неполными типами([Basic.fundamental]). Объекты не должны быть определены так, чтобы иметь неполный тип.
Имя класса вставляется в область, в которой она объявлена сразу после просмотра имени класса. Название класса также вставлен в сферу действия самого класса; это известно как впрыскивается класс имя. В целях проверки доступа inted-class-name рассматривается как имя публичного участника. Спецификатор класса обычно называется определением класса. Класс определяется после закрытия скобки его спецификатора класса был замечен, хотя его функции-члены еще не созданы определены. Необязательный атрибут-спецификатор-seq относится к классу; после этого атрибуты в атрибуте-спецификаторе-seq считанные атрибуты класса всякий раз, когда он назван.
Текст, выделенный жирным шрифтом, создает простую картину того, что рассматриваемые компиляторы рассматривают параметр типа T как неполный тип объекта. Это как если бы вы только провозгласили это, вот так:
class derived;
Они не могут вывести, что это форвардное объявление - это класс, полученный из base
. Поэтому они не могут принять его как тип возвращаемого варианта совместного использования в контексте foo_default_impl
. Как было указано @marcinj в комментариях:
Если тип класса в ковариантном типе возврата D:: f отличается от то из B:: f, тип класса в возвращаемом типе D:: f должен быть завершена в точке объявления D:: f или будет классом тип D.
Так как T
не является полным и не является самим foo_default_impl<T>
, он не может быть ковариантным возвращаемым типом.