Is there a "dynamic type"?

This question is related to decltype and multiple inheritance.

Suppose I have the following:

  • abstract class A with several virtual methods,
  • several derived classes that implement methods using the previous virtual ones (each of these classes is a precedent)
  • a final concrete class that inherits from a subset of previous use cases and implements pure virtual methods.

For instance:

 #include <iostream> /** * "Iterable container" */ template <class T> struct A { virtual T* data() =0; virtual const T* data() const =0; virtual unsigned size() const =0; T* begin() { return data(); } T* end() { return data()+size(); } const T* begin() const { return data(); } const T* end() const { return data()+size(); } }; // ------------------------------------------------------------------------ /** * Iterative assignment */ template <class T> struct B: public A<T> { auto operator =( const T& val ) -> decltype(*this) { for ( auto& v: *this ) v = val; return *this; } }; /** * Iterative display */ template <class T> struct C: public A<T> { void show() const { for ( auto& v: *this ) std::cout<< v << " "; std::cout<< std::endl; } }; // ------------------------------------------------------------------------ /** * Concrete implementation */ template <class T, unsigned N> struct D: public B<T>, public C<T> { using B<T>::operator=; T dat[N]; T* data() { return dat; } const T* data() const { return dat; } unsigned size() const { return N; } }; // ------------------------------------------------------------------------ int main() { D<double,5> d; (d = 42).show(); // compile-time error, "no member named 'show' in 'B<double>'" } 

The problem is that this is not a pun; if one of the use-case methods should return a reference to *this , I would like this be a reference to the final concrete class, so that I could link the call to other methods from other use cases.

However, with the previous implementation, I get a compile-time error. Is there any other way to achieve what I explained?

+6
source share
2 answers

The solution is to use CRTP; you tell B to return the lvalue reference to D<T, N> , passing the most derived type as an additional template parameter.

 template <class T, class Derived> struct B: public A<T> { auto operator =( const T& val ) -> Derived& // ... template <class T, unsigned N> struct D: public B<T, D<T, N>>, // ... 
+4
source

You can give D overriding operator= , which returns D & :

 auto operator =( const T& val ) -> decltype(*this) override { B<T>::operator=(val); return *this; } 

This works because D & covariant with B & , and overriding functions must have a covariant return type. This is currently a shadow of B operator= , because it is not virtual .

+3
source

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


All Articles