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!
CodePudding user response:
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>();