Подтвердить что ты не робот

Ковариантный тип возврата не распознается

По какой-то причине обновленные версии GCC и clang не распознают ковариацию возвращаемого типа в этом конкретном сценарии. Сообщение об ошибке вводит в заблуждение:

error: return type of virtual function 'foo' is not covariant with the return
     type of the function it overrides ('derived *' is not derived from 'base *')

Вот код:

class base
{
private:
  virtual base * foo() = 0;
};

template< class T >
class foo_default_impl : public virtual base
{
private:
  T * foo() override { return nullptr; }
};

class derived : public virtual base, private foo_default_impl< derived >
{
};

int main() {
  derived d{}; // error: return type of virtual function 'foo' is not covariant with the return type of the function it overrides ('derived *' is not derived from 'base *')
  return 0;
}
4b9b3361

Ответ 1

Вот вещь. В то время как для нас может показаться, что компилятор знает все, что ему нужно знать о типах, о которых идет речь, стандарт говорит иначе.

[temp.arg.type/2]

... [Примечание: Аргумент типа шаблона может быть неполным типом. - конечная нота]

[basic.types/5]

Класс, который был объявлен, но не определен, тип перечисления в определенные контексты ([dcl.enum]) или массив неизвестной границы или тип неполного элемента - это не полностью определенный тип объекта.46 Неполно определенные типы объектов и cv void являются неполными типами([Basic.fundamental]). Объекты не должны быть определены так, чтобы иметь неполный тип.

[класс/2]

Имя класса вставляется в область, в которой она объявлена сразу после просмотра имени класса. Название класса также вставлен в сферу действия самого класса; это известно как впрыскивается класс имя. В целях проверки доступа inted-class-name рассматривается как имя публичного участника. Спецификатор класса обычно называется определением класса. Класс определяется после закрытия скобки его спецификатора класса был замечен, хотя его функции-члены еще не созданы определены. Необязательный атрибут-спецификатор-seq относится к классу; после этого атрибуты в атрибуте-спецификаторе-seq считанные атрибуты класса всякий раз, когда он назван.

Текст, выделенный жирным шрифтом, создает простую картину того, что рассматриваемые компиляторы рассматривают параметр типа T как неполный тип объекта. Это как если бы вы только провозгласили это, вот так:

class derived;

Они не могут вывести, что это форвардное объявление - это класс, полученный из base. Поэтому они не могут принять его как тип возвращаемого варианта совместного использования в контексте foo_default_impl. Как было указано @marcinj в комментариях:

[class.virtual/8]

Если тип класса в ковариантном типе возврата D:: f отличается от то из B:: f, тип класса в возвращаемом типе D:: f должен быть завершена в точке объявления D:: f или будет классом тип D.

Так как T не является полным и не является самим foo_default_impl<T>, он не может быть ковариантным возвращаемым типом.