Search code examples
c++vector3d

c++ templated struct casting


I am trying to develop my own 3d renderer, and I need a 3D vector struct for this. To do this, I need to get a Vector<3, double>, multiply it to a 4x4 matrix, and then convert it to a Vector<2, int> that represents the coordinates of the pixel on the screen.

template <size_t DIM, typename T>
struct Vec
{
    // Vector components
    T data[DIM];
    Vec() : data() {}

    Vec(std::vector<T> _data)
    {
        assert(_data.size() == DIM);
        for (int i = DIM; i--; data[i] = _data[i])
            ;
    }

    // Convert Vec<DIM, T> -> Vec<DIM, U>
    template <typename U>
    Vec(Vec<DIM, U> &v)
    {
        for (int i = 0; i < DIM; i++)
            data[i] = (T)v[i];
    }

    T &operator[](size_t idx)
    {
        return data[idx];
    }
};

template <size_t LEN, size_t DIM, typename T>
Vec<LEN, T> embed(const Vec<DIM, T> &v, T fill = 1)
{
    Vec<LEN, T> ret;
    for (size_t i = LEN; i--; ret[i] = (i < DIM ? v[i] : fill))
        ;
    return ret;
}

template <size_t LEN, size_t DIM, typename T>
Vec<LEN, T> proj(const Vec<DIM, T> &v)
{
    Vec<LEN, T> ret;
    for (size_t i = LEN; i--; ret[i] = v[i])
        ;
    return ret;
}

But when I try to do this:

int main()
{
    Vec<3, float> x({0.8, 4.3, 3.3});
    Vec<4, float> v0 = embed<4>(x);
    Vec<2, float> v1 = proj<2>(v0);
    Vec<2, int> y = (Vec<2, int>)(v1);

    std::cout << x[0] << '\n';
    std::cout << y[0] << '\n';
    return 0;
}

I get this error:

main.cpp: In instantiation of ‘Vec<LEN, T> embed(const Vec<DIM, T>&, T) [with long unsigned int LEN = 4; long unsigned int DIM = 3; T = float]’:
main.cpp:55:31:   required from here
main.cpp:38:49: error: passing ‘const Vec<3, float>’ as ‘this’ argument discards qualifiers [-fpermissive]
   38 |  for (size_t i = LEN; i--; ret[i] = (i < DIM ? v[i] : fill))
      |                                                ~^
main.cpp:28:5: note:   in call to ‘T& Vec<DIM, T>::operator[](size_t) [with long unsigned int DIM = 3; T = float; size_t = long unsigned int]’
   28 |  T &operator[](size_t idx)
      |     ^~~~~~~~

What does this mean? How can I avoid this error? Thanks!


Solution

  • const Vec<DIM, T> &v - this is a reference to a constant vector.

    T &operator[](size_t idx) - this is a subscript operator of a non constant vector.

    Add yet another, overloaded subscript operator const T &operator[](size_t idx) const - this is a subscript operator of a constant vector:

        T &operator[](size_t idx)
        {
            return data[idx];
        }
    

    With the Explicit object parameter (deducing this) in C++23 you will be able to replace that two subscript operators with a single one:

        auto &&operator[](this auto&& self, size_t idx)
        {
            return self.data[idx];
        }
    

    This C+23 feature is not supported yet by GCC and Clang, MSVC supports it with /std:c++latest but not with /std:c++23: https://godbolt.org/z/onr7GK6x5. See the supported features by the compilers: C++23 core language features.

    main.cpp:38:49: error: passing ‘const Vec<3, float>’ as ‘this’ argument discards qualifiers [-fpermissive]
       38 |  for (size_t i = LEN; i--; ret[i] = (i < DIM ? v[i] : fill))
          |                                                ~^
    main.cpp:28:5: note:   in call to ‘T& Vec<DIM, T>::operator[](size_t) [with long unsigned int DIM = 3; T = float; size_t = long unsigned int]’
       28 |  T &operator[](size_t idx)
          |     ^~~~~~~~
    

    The error explains: for const Vec<DIM, T> &v, this in v functional members is const Vec<DIM, T> *this, and T &operator[](size_t idx) may not be used with constant vector objects.