I’m trying to write a utility header for a linear algebra program, it’s mainly for learning so I’m more concerned about ease than efficiency.
Anyways, the idea is that I have a Matrix type, and a Vector type which inherits from Matrix. Because I want to add some specialized function especially for the 3D case, without rewriting the whole class, I also have a _Vector class which is meant to be inherited by both the general and special type.
Here is a minimal example of the failure:
(fails.hpp)
#include <string>
template <int M, int N, typename FloatType = double>
class Matrix {
public:
inline Matrix() : data{0}{}
protected:
FloatType data[M * N];
};
template <int DIM, typename FloatType>
class _Vector : public Matrix<DIM, 1, FloatType> {
public:
template <typename ...FloatTypes>
inline constexpr _Vector(const FloatTypes... values)
: Matrix<sizeof...(FloatTypes), 1, FloatType>()
{
int i = 0;
((Matrix<DIM, 1, FloatType>::data[i++] = values), ...);
}
inline FloatType operator [](const int index) {
return Matrix<DIM, 1, FloatType>::data[index];
}
inline operator std::string () const {
std::string tmp = "[ ";
for (int i = 0; i < DIM; i++) {
tmp += std::to_string(Matrix<DIM, 1, FloatType>::data[i]) + " ";
}
tmp += "]\n";
return tmp;
}
};
template<int DIM, typename FloatType = double>
class Vector : _Vector<DIM, FloatType> {
template <typename ...FloatTypes>
inline constexpr Vector(const FloatTypes... values) : _Vector<DIM, FloatType>(values...) {}
};
template <typename ...FloatTypes>
Vector(const FloatTypes...) -> Vector<sizeof...(FloatTypes), std::common_type_t<FloatTypes...>>;
template <typename FloatType>
class Vector<3, FloatType> : public _Vector<3, FloatType> {
public:
FloatType& x = Matrix<3, 1, FloatType>::data[0];
FloatType& y = Matrix<3, 1, FloatType>::data[1];
FloatType& z = Matrix<3, 1, FloatType>::data[2];
template <typename ...FloatTypes>
inline constexpr Vector(const FloatTypes... values) : _Vector<3, FloatType>(values...) {}
// cross product
inline Vector operator ^(_Vector<3, FloatType>& other) {
Vector V;
V.x = y * other[2] - z * other[1];
V.y = z * other[0] - x * other[2];
V.z = x * other[1] - y * other[0];
return V;
}
};
(main.cpp)
#include <fails.hpp>
//#include <works.hpp>
#include <iostream>
int main() {
Vector v1 = { 1., 2., 3. };
Vector v2 = { 3., 2., 1. };
std::cout << (std::string)(v1 ^ v2);
}
What I don’t understand is that if I remove the inheritance from the Matrix type, it fixes the issue:
(works.hpp)
#include <string>
template <int DIM, typename FloatType>
class _Vector {
public:
template <typename ...FloatTypes>
inline constexpr _Vector(const FloatTypes... values)
{
int i = 0;
((data[i++] = values), ...);
}
inline FloatType operator [](const int index) {
return data[index];
}
inline operator std::string () const {
std::string tmp = "[ ";
for (int i = 0; i < DIM; i++) {
tmp += std::to_string(data[i]) + " ";
}
tmp += "]\n";
return tmp;
}
protected:
FloatType data[DIM];
};
template<int DIM, typename FloatType = double>
class Vector : _Vector<DIM, FloatType> {
template <typename ...FloatTypes>
inline constexpr Vector(const FloatTypes... values) : _Vector<DIM, FloatType>(values...) {}
};
template <typename ...FloatTypes>
Vector(const FloatTypes...) -> Vector<sizeof...(FloatTypes), std::common_type_t<FloatTypes...>>;
template <typename FloatType>
class Vector<3, FloatType> : public _Vector<3, FloatType> {
public:
FloatType& x = Vector<3, FloatType>::data[0];
FloatType& y = Vector<3, FloatType>::data[1];
FloatType& z = Vector<3, FloatType>::data[2];
template <typename ...FloatTypes>
inline constexpr Vector(const FloatTypes... values) : _Vector<3, FloatType>(values...) {}
// cross product
inline Vector operator ^(_Vector<3, FloatType>& other) {
Vector V;
V.x = y * other[2] - z * other[1];
V.y = z * other[0] - x * other[2];
V.z = x * other[1] - y * other[0];
return V;
}
};
When I compile with g++ -I. main.cpp -std=c++17 I get this output:
n file included from main.cpp:1:
./fails.hpp: In instantiation of ‘constexpr _Vector<DIM, FloatType>::_Vector(const FloatTypes ...) [with FloatTypes = {}; int DIM = 3; FloatType = double]’:
./fails.hpp:54:88: required from ‘Vector<3, FloatType> Vector<3, FloatType>::operator^(_Vector<3, FloatType>&) [with FloatType = double]’
main.cpp:9:35: required from here
./fails.hpp:58:12: in ‘constexpr’ expansion of ‘V.Vector<3, double>::Vector<>()’
./fails.hpp:16:51: error: type ‘Matrix<0, 1, double>’ is not a direct base of ‘_Vector<3, double>’
16 | : Matrix<sizeof...(FloatTypes), 1, FloatType>()
|
I don’t know how the inheritance changed the situation at all but it now seems like the _Vector class constructor is called for some reason (with wrong template parameters at that). I don’t know what to search for, so if you can explain why it causes this issue and in general what type of problem this is I’d be very grateful.
Thank you!
>Solution :
Your _Vector<int DIM, typename FloatType> inherits Matrix<DIM, FloatType>, but its constructor uses sizeof...(FloatTypes) as the first template parameter of Matrix
template <int DIM, typename FloatType>
class _Vector : public Matrix<DIM, 1, FloatType>
public:
template <typename ...FloatTypes>
constexpr _Vector(const FloatTypes... values)
: Matrix<sizeof...(FloatTypes), 1, FloatType>();
};
This means that when DIM is not equal to sizeof...(FloatTypes), your _Vector initializes a base that does not inherit from it, you should define the constructor as
template <typename ...FloatTypes>
constexpr _Vector(const FloatTypes... values)
: Matrix<DIM, 1, FloatType>();