Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Covariant return types, const-ness, and incomplete classes

Tags:

c++

covariance

This code compiles successfully under g++ 6.1 but gives an error with clang 3.8:

class C;

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

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

The error from clang is as follows:

$ 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)

If class C is completely defined rather than forward-declared, there is no error. My understanding is that covariant allows "lesser" cv-qualification (ie, dropping the const from the return type) when overriding a virtual method.

Is clang correct / allowed to require a complete type, and if so, why ? How can having the definition of C available change anything here ?

This is not totally academic, in a large code base I am reluctant to add in unnecessary includes, we try to forward declare as standard practice.

like image 491
Chris Morley Avatar asked Jul 22 '16 19:07

Chris Morley


2 Answers

This is clang 3.8 bug, specifically 26297. From [class.virtual], wording from N4594:

The return type of an overriding function shall be either identical to the return type of the overridden function or covariant with the classes of the functions. If a function D::f overrides a function B::f, the return types of the functions are covariant if they satisfy the following criteria: (7.1) — both are pointers to classes, both are lvalue references to classes, or both are rvalue references to classes
(7.2) — the class in the return type of B::f is the same class as the class in the return type of D::f, or is an unambiguous and accessible direct or indirect base class of the class in the return type of D::f
(7.3) — both pointers or references have the same cv-qualification and the class type in the return type of D::f has the same cv-qualification as or less cv-qualification than the class type in the return type of B::f.

Having B::f return C const* and D::f return C* matches all of these requirements (neither pointer is cv-qualified, and the class type of D::f is less cv-qualified than the base), hence it should be allowed.

There is no requirement on completeness; C does not need to be complete to check that these criteria.

like image 163
Barry Avatar answered Oct 20 '22 23:10

Barry


I also find nothing wrong with your code. It compiles with the head version of clang and all compilers I tried, except clang 3.8 and earlier.

Live demo

The relevant standard text:

10.3 note 8:

If the class type in the covariant return type of D::f differs from that of B::f, the class type in the return type of D::f shall be complete at the point of declaration of D::f or shall be the class type D.

The class type of the covariant methods must be the same or complete, but as I understand it const/volatile differences are still considered the same class type, making your example legal.

like image 26
Johan Lundberg Avatar answered Oct 20 '22 23:10

Johan Lundberg