Ковариантные типы возврата, константа и неполные классы

Этот код успешно компилируется в g++ 6.1, но дает ошибку с clang 3.8:

class C;

class Base {
public:
  virtual const C *getC();
};

class Derived : public Base {
public:
  virtual       C *getC();
};

Ошибка от clang выглядит следующим образом:

$ dev/compilers/linux-x86_64-2.12.2/clang3.8/bin/clang++ -Wall -c testcovariantreturn.cxx
testcovariantreturn.cxx:10:20: error: return type of virtual function 'getC' is not covariant with the return type of the function it overrides ('C' is incomplete)

Если класс C полностью определен, а не форвардно объявлен, ошибки не возникает. Мое понимание заключается в том, что ковариант допускает "меньшую" cv-квалификацию (т.е. Отбрасывание константы из возвращаемого типа) при переопределении виртуального метода.

Правильно ли clang/разрешено требовать полный тип, и если да, то почему? Как можно здесь изменить определение C?

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

Ответы

Ответ 1

Это ошибка 3.8, особенно 26297. Из [class.virtual], формулировка из N4594:

Возвращаемый тип функции переопределения должен быть либо идентичен возвращаемому типу переопределенной функции или ковариантно с классами функций. Если функция D::f переопределяет функцию B::f, возвращаемые типы функций ковариантны, если они удовлетворяют следующим критериям: (7.1) - оба являются указателями на классы, оба являются значениями lvalue для классов или оба являются значениями rvalue для классы
(7.2) - класс возвращаемого типа B::f является тем же классом, что и класс возвращаемого типа D::f, или является однозначный и доступный прямой или косвенный базовый класс класса в возвращаемом типе D::f
(7.3) - оба указателя или ссылки имеют одинаковую cv-квалификацию и тип класса в возвращаемом типе D::fимеет ту же квалификацию cv как или менее cv-qualification, чем тип класса в возвращаемом типе B::f.

Имея B::f return C const* и D::f return C* соответствует всем этим требованиям (ни один из указателей не является cv-квалификатором, а тип класса D::f меньше CV, чем базовый) следовательно, это должно быть разрешено.

Нет требований к полноте; C не нужно заполнять, чтобы проверить, что эти критерии.

Ответ 2

Я также не вижу ничего плохого в вашем коде. Он компилируется с главной версией clang и всех компиляторов, которые я пробовал, кроме clang 3.8 и ранее.

Живая демонстрация

Соответствующий стандартный текст:

10.3 примечание 8:

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

Тип класса ковариантных методов должен быть одинаковым или но, как я понимаю, все еще считаются константы/неустойчивые различия  тот же тип класса, что делает ваш пример законным.