Invalid covariant return type errors in nested classes with methods that return template-based objects

The following C ++ code gives me these compilation errors:

covariant.cpp:32:22: error: invalid covariant return type for 'virtual Q<B> C::test()' covariant.cpp:22:22: error: overriding 'virtual Q<A> B::test()' 

I do not want to change the string virtual Q<B> test() {} to virtual Q<A> test() {} , although it removes compilation errors. Is there any other way to solve this problem?

 template <class T> class Q { public: Q() {} virtual ~Q() {} }; class A { public: A() {} virtual ~A() {} }; class B { public: B() {} virtual ~B() {} virtual Q<A> test() = 0; }; class C : public B { public: C() {} virtual ~C() {} virtual Q<B> test() {} }; 
+4
source share
3 answers

Q<B> and Q<A> are unrelated classes. Imagine that you are the client of B calling test() : what do you assign to the result if you do not know what type it will have?

The fact that both Q<A> and Q<B> are instances of the same class template does not alter the fact that they are two completely unrelated classes, possibly with a completely different layout (due to specialized specialization).

This will not be different from this:

 struct X { virtual std::string test() = 0; }; struct Y : X { virtual int test() { return 42; } // ERROR! std::string and int are // unrelated, just as Q<A> and Q<B> }; 

The client calling test() with a pointer to X expects the result to be string , but the "Whoops!" Object that this pointer points to is of type Y , and return type Y::test() is int . What is going to happen? Error at runtime?

 Y y; X* p = &y; std::string s = p->test(); // D'OH! 

C ++ is a statically typed language, which means that type checking is done at compile time. In this case, a message from the compiler should tell you that the derived class does not adhere to the interface of the class from which it comes.

If you're curious about what the “invalid covariant return type” means and, in particular, the word “covariant”, this is easily explained.

Suppose you have a base class B with a virtual function foo() that returns X* :

 struct B { virtual X* foo(); }; 

And suppose you have a class D derived from B that overrides foo() , returning Y* , where Y is the class derived from X :

 struct D : B { virtual Y* foo(); }; 

This is problem? Well, the correct answer comes from answering this slightly better question: "Will this be a problem for a client calling foo() who is expecting X* return?"

And the answer to this question is obviously No, since Y is a derived class of X , so you can return a pointer to Y instead of a pointer to X :

 D d; B* b = &d; X* p = b->foo(); // Returns an Y*, but that OK, because a pointer to Y can be // assigned to a pointer to X 

This is an example of a covariant return type. In your example, the return type C::test() not covariant with respect to the return type B::test() .

+5
source

A function with signature B::test(void) returns an object of type Q<A> , and C::test(void) (which is the same signature, so you rewrite the function) returns Q<B> . I think that this is impossible.

As far as I know, it is impossible to overload a function using the return type, and rewriting parent functions must adhere to the same return type.

From the standard §10.3 / 7

The return type of an overriding function must be either identical to the return type of an overridden function, or covariant with function classes. If the function D :: f overrides the function B :: f, the returned function types are covariant if they satisfy the following criteria:

  • both are pointers to classes, both are lvalue values ​​for classes, or both are rvalue values ​​for classes112
  • the class in the return type B :: f is the same class as the class of the return type D :: f, or is the unambiguous and accessible direct or indirect base class of the class in the return type D :: e
  • both pointers or references have the same cv qualification, and the class type in the return type D :: f has the same cv qualification as or less cv-qualification than the class type in the return type B :: e.
+1
source

You cannot do this. Overriding virtual functions cannot change the prototype of a function, except in very specific cases, such as covariant return types.

The return of covariants will be valid if you return in a virtual redefinition of the subclass of the type returned to the virtual base. But your Q<A> and Q<B> not inherited. The fact that B is a subclass of A is irrelevant here.

0
source

Source: https://habr.com/ru/post/1487644/


All Articles