Home > Mobile >  Template superclass constructor called when converting to base
Template superclass constructor called when converting to base

Time:03-06

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>();
  • Related