Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a "dynamic decltype"?

This question is related to decltype and multiple inheritance.

Assume I have the following:

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

For example:

#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 this (no pun intended); if one of the "use-case" method should return a reference to *this, I would like this to be a reference to the final concrete class, so that I could chain the call with other methods from other use-cases.

With the previous implementation however, I'm getting a compile-time error. Is there another way to achieve what I explained?

like image 674
Jonathan H Avatar asked Aug 26 '14 13:08

Jonathan H


1 Answers

The solution is to use CRTP; you tell B to return an lvalue reference to D<T, N> by 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>>,
    // ...
like image 71
ecatmur Avatar answered Sep 19 '22 11:09

ecatmur