Museful
Museful

Reputation: 6959

How to reinterpret or cast an object with known memory layout without getting Undefined Behavior

I have a matrix class with a transposedView() method that I have been using for years as a "zero overhead" conversion between row and column vectors.

template<int M, int N=M, typename T = double>
struct mat {
    std::array<T,M*N> buf;
    // ...
    template<int Md = M, int Nd = N, typename = std::enable_if_t<Md == 1 || Nd == 1>>
    const mat<N, M, T>& transposedView() const {
        static_assert(M == 1 || N == 1, "transposedView() supports only vectors, not general matrices.");
        return *reinterpret_cast<const mat<N, M, T>*>(this);
    }
}

I used to trust this because the memory layout of mat<1,N> corresponds exactly to mat<N,1>, but I have just learned that this function has Undefined Behavior. Do you have any advice on what I could replace the contents/implementation of this function with?

Upvotes: 3

Views: 441

Answers (1)

xskxzr
xskxzr

Reputation: 13040

You can design a new view class template, like std::string_view corresponding to std::string, or Eigen::Map corresponding to Eigen::Matrix (so you can see, this is a common design in C++). The view class template may simply hold a pointer pointing to the beginning of the sequence it views and a size (if you only want to support a view for a whole matrix, you can omit the size member as it can be inferred from its template parameters). Your transposedView function can return such a view class instead.

template<int M, int N=M, typename T = double>
struct mat_view {
    T *begin;
    std::size_t size;
    // member functions you want to support
    // ...
}

template<int M, int N=M, typename T = double>
struct mat {
    std::array<T,M*N> buf;
    // ...
    template<int Md = M, int Nd = N, typename = std::enable_if_t<Md == 1 || Nd == 1>>
    const mat_view<N, M, T> transposedView() const {
        static_assert(M == 1 || N == 1, "transposedView() supports only vectors, not general matrices.");
        return {std::begin(buf), M * N};
    }
}

Upvotes: 1

Related Questions