Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Template superclass constructor called when converting to base

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)

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

#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>();
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading