Ковариантные типы возврата, константа и неполные классы
Этот код успешно компилируется в 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.
Тип класса ковариантных методов должен быть одинаковым или
но, как я понимаю, все еще считаются константы/неустойчивые различия
тот же тип класса, что делает ваш пример законным.