Explicit Object Member Functions (A.k.a Deducing this)#

Problem: Repeated Code#

  • Typical problem: (non)``const`` getter overloads * ⟶ Repeated code

#include <vector>
#include <iostream>

class Matrix
{
public:
    Matrix(unsigned xdim, unsigned ydim)
    : _xdim(xdim), _ydim(ydim), _data(xdim*ydim) {}

    const int& at(unsigned x, unsigned y) const
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return _data.at(y*_ydim + x);                  // <-- duplicate code
    }
    int& at(unsigned x, unsigned y)
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return _data.at(y*_ydim + x);                  // <-- duplicate code
    }

private:
    unsigned _xdim, _ydim;
    std::vector<int> _data;
};

int main()
{
    Matrix m(2,2);
    m.at(1,1) = 42;
    std::cout << m.at(1,1) << std::endl;               // <-- non-const at()
    const Matrix& cm = m;
    std::cout << cm.at(1,1) << std::endl;              // <-- const at()
    return 0;
}

Python Excursion: Explicit Object Parameter#

  • In Python, there is no implicit this

  • According to Zen, Explicit is better than implicit (here)

  • Also, in Python, there is no overloading

  • Problem solved

  • (Assignment to [] expressions works differently in Python)

class Matrix:
    def __init__(self, xdim, ydim):
        self._xdim = xdim
        self._ydim = ydim
        self._data = [0 for _ in range(xdim*ydim)]
    def at(self, x, y):
        return self._data[y*self._ydim + x]
    def set(self, x, y, value):
        self._data[y*self._ydim + x] = value        

m = Matrix(2,2)
m.set(1,1, 42)
print(m.at(1,1))

C++: Explicit Object Parameter (Since C++23)#

  • Really bad syntax

  • this is only an annotation: “pass the object as (const) reference”

  • ⟶ No const method, but const object instead

  • Still duplicate code!

#include <vector>
#include <iostream>

class Matrix
{
public:
    Matrix(unsigned xdim, unsigned ydim)
    : _xdim(xdim), _ydim(ydim), _data(xdim*ydim) {}

    const int& at(unsigned x, unsigned y) const
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return _data.at(y*_ydim + x);                  // <-- duplicate code
    }
    int& at(unsigned x, unsigned y)
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return _data.at(y*_ydim + x);                  // <-- duplicate code
    }

private:
    unsigned _xdim, _ydim;
    std::vector<int> _data;
};

int main()
{
    Matrix m(2,2);
    m.at(1,1) = 42;
    std::cout << m.at(1,1) << std::endl;               // <-- non-const at()
    const Matrix& cm = m;
    std::cout << cm.at(1,1) << std::endl;              // <-- const at()
    return 0;
}

Template Method To Deduplicate#

  • Return type deduction

  • Deduced from selected vector::at() method

#include <vector>
#include <iostream>

class Matrix
{
public:
    Matrix(unsigned xdim, unsigned ydim)
    : _xdim(xdim), _ydim(ydim), _data(xdim*ydim) {}

    template <typename Self>
    auto& at(this Self& self, unsigned x, unsigned y)
    {
        return self._data.at(y*self._ydim + x);
    }

private:
    unsigned _xdim, _ydim;
    std::vector<int> _data;
};

int main()
{
    Matrix m(2,2);
    m.at(1,1) = 42;
    std::cout << m.at(1,1) << std::endl;
    const Matrix& cm = m;
    std::cout << cm.at(1,1) << std::endl;
    return 0;
}

Even Shorter: Abbreviated Function Template#

  • Cool

  • Readable?

#include <vector>
#include <iostream>

class Matrix
{
public:
    Matrix(unsigned xdim, unsigned ydim)
    : _xdim(xdim), _ydim(ydim), _data(xdim*ydim) {}

    auto& at(this auto& self, unsigned x, unsigned y)
    {
        return self._data.at(y*self._ydim + x);
    }

private:
    unsigned _xdim, _ydim;
    std::vector<int> _data;
};

int main()
{
    Matrix m(2,2);
    m.at(1,1) = 42;
    std::cout << m.at(1,1) << std::endl;
    const Matrix& cm = m;
    std::cout << cm.at(1,1) << std::endl;
    return 0;
}