1
votes

Problem

I am writing a small math library that has Vector and Matrix classes. I'd like to make it convenient to intialize objects of these classes with strict rules for how they can be initialized.

The Vector class constructor already works as i intended. It is restricted such that only the correct size N is accepted and on top of that all arguments are of the same type (see below).

Vector

template <std::size_t N, typename T>
class Vector{
  public:
    template <typename... TArgs,
            std::enable_if_t<
                sizeof...(TArgs) == N &&
                    (std::is_same_v<T, std::remove_reference_t<TArgs>> && ...),int> = 0>
    Vector(TArgs &&... args) : data_{std::forward<TArgs>(args)...} {}

    // ...

  private:
    std::array<T,N> data;
}

Example initialization (works, is restricted)

Vector<3,int> v{1,2,3}

Matrix

template <std::size_t N, std::size_t M, typename T>
class Matrix{
  public:
    /* put restriction rules here */
    Matrix(std::initializer_list<std::initializer_list<T>> lst) {
      int i = 0;
      int j = 0;
      for (const auto &l : lst) {
        for (const auto &v : l) {
          data[i][j] = v;
          ++j;
        }
        j = 0;
        ++i;
      }
    }

    // ...

  private:
    std::array<Vector<M, T>, N> data;
}

Example initialization 1 (works, is not restricted)

Matrix<2,3,int> m{ {1,2,3}, {4,5,6} }

Example initialization 2 (compiles, but shouldn't!!)

Matrix<2,3,int> m{ {1,'a',3}, {4,5,6,7,8,9} }

Question

I wasn't able to implement the Matrix class constructor for the double-nested initialization with a variadic template, so i used nested std::initializer_list´s (from this post). It works, but i'd like to have the same restrictions on this constructor as for the Vector class.

How can i do this?

  • the list size must be N (rows)
  • the list-list size must always be M (columns)
  • the encapsulated value type must always be T

additional info

regarding this post here... ::std::initializer_list vs variadic templates i can say that i don't really care for using initializer_list or a variadic template. Either one seems fine in this case, but as far as i understand getting the size of an std::initializer_list at compile time is somewhat difficult.

2

2 Answers

2
votes

You can simple make Matrix take a 2D array reference as such:

Matrix(T const (&m)[N][M])

This will only compile under the conditions you specify and adapting the rest of your code is also easy. You can apply the same principle to simplify Vector.

1
votes

To have you desired syntax, you might do:

template <typename ... Us,
          std::enable_if_t<sizeof...(Us) == N && (std::is_same_v<Us, T> && ...), int> = 0>
Matrix(const Us (&... rows)[M]) {
    // ...
}