Using auto and decltype for making a function return the type of its class. How can I make it return a value, instead of a reference?

I am trying to make a generic, purely virtual Matrix class that supports methods that return a mew Matrix. Of course, if one of those methods is used on a subclass of Matrix it should return something of the subclass type, instead of a Matrix.

Currently my code is something like this:

class Matrix
{
    virtual auto transposed() const -> decltype (*this) = 0 ;
} ;

class DenseMatrix : Matrix
{
    auto transposed() const -> decltype (*this)
    {
        DenseMatrix res ;
        // Do some magic

        return res ;
    }
} ;

However, since decltype(*this) is of type DenseMatrix& instead of DenseMatrix, the code fails because it ends up returning a reference to a local variable.

How can I tell C++ that I want to return a value instead of a reference? Alternatively, is there any cleaner way of achieving virtual functions returning the type of the class they are being called from?

Answers


The following is off the cuff, and not fully tested. However I find myself wondering if the well known handle/body idiom might be a good match for your requirements here. It works by making Matrix nothing but a pointer to an implementation (what you currently call Matrix). And then derived clients derive from the implementation, not from Matrix itself.

Here's a small sketch:

#include <memory>

class Matrix
{
public:
    class Imp
    {
    public:
        virtual ~Imp() = default;
        virtual auto transposed() const -> std::unique_ptr<Imp> = 0;
    };

private:
    std::unique_ptr<Imp> data_;
public:
    Matrix() = default;

    explicit Matrix(std::unique_ptr<Imp> data)
        : data_(std::move(data)) {}

    auto transposed() const -> Matrix
    {
        return Matrix(data_ ? data_->transposed() : nullptr);
    }
};

class DenseMatrix
    : public Matrix::Imp
{
public:
    virtual auto transposed() const -> std::unique_ptr<Imp>
    {
        std::unique_ptr<DenseMatrix> res(new DenseMatrix);
        // Do some magic
        return std::move(res);
    }
};

It means all of your data must be on the heap. But that is probably going to happen anyway in a situation like this.

External clients just deal with Matrix, which is not a base class, but a "handle" to a pointer to the base class. New implementations derive from the internal base class instead of from the Matrix class itself.

It doesn't work all of the time. And I've glossed over details like how the client is going to specify he wants (e.g.) DenseMatrix as opposed to some other type of Matrix. But this general data structure can sometimes be quite useful.


If you want to remove the reference from a type, you could simply use std::remove_reference.

Yet, another general solution to this kind of problem is to use static polymorphism aka CRTP:

struct MatrixBase
{
     // here goes all stuff which is independent of the actual matrix,
     // like number of rows, columns, etc.
};

template<typename Derived>
struct Matrix : public MatrixBase
{
     virtual auto transposed() const -> Derived
     {
           return static_cast<Derived const&>(*this).transposed();
     }
     // ...
};

struct DenseMatrix : public Matrix<DenseMatrix>
{
     virtual auto transposed() const override -> DenseMatrix
     { /* implementation */ }
     // ...
};

Such a strategy is usually a bit faster than using dynamic polymorphism vis base class pointers. CRTP is used, for instance, also by the library Eigen.


Need Your Help

How to increase JSON variable length?

javascript php arrays json

I am making a chart for a school website and it uses a JSON variable as data of the chart.

Unwanted database during Azure deploy

c# asp.net entity-framework asp.net-mvc-4 azure

I try to deploy my project into Azure, and while the publishing is successful, I always get the "A network-related or instance-specific error occurred while establishing a connection to SQL Server. (