9

I created the following Matrix class:

template <typename T>
class Matrix
{
    static_assert(std::is_arithmetic<T>::value,"");

public:
    Matrix(size_t n_rows, size_t n_cols);
    Matrix(size_t n_rows, size_t n_cols, const T& value);

    void fill(const T& value);
    size_t n_rows() const;
    size_t n_cols() const;

    void print(std::ostream& out) const;

    T& operator()(size_t row_index, size_t col_index);
    T operator()(size_t row_index, size_t col_index) const;
    bool operator==(const Matrix<T>& matrix) const;
    bool operator!=(const Matrix<T>& matrix) const;
    Matrix<T>& operator+=(const Matrix<T>& matrix);
    Matrix<T>& operator-=(const Matrix<T>& matrix);
    Matrix<T> operator+(const Matrix<T>& matrix) const;
    Matrix<T> operator-(const Matrix<T>& matrix) const;
    Matrix<T>& operator*=(const T& value);
    Matrix<T>& operator*=(const Matrix<T>& matrix);
    Matrix<T> operator*(const Matrix<T>& matrix) const;

private:
    size_t rows;
    size_t cols;
    std::vector<T> data;
};

I tried to use a matrix of std::complex:

Matrix<std::complex<double>> m1(3,3);

The problem is that the compilation fails (static_assert fails):

$ make
g++-mp-4.7 -std=c++11   -c -o testMatrix.o testMatrix.cpp
In file included from testMatrix.cpp:1:0:
Matrix.h: In instantiation of 'class Matrix<std::complex<double> >':
testMatrix.cpp:11:33:   required from here
Matrix.h:12:2: error: static assertion failed: 
make: *** [testMatrix.o] Error 1

Why std::complex is not an arithmetic type? I want to enable the utilisation of unsigned int (N), int (Z), double (R), std::complex (C) and maybe some home made class (e.g. a class representing Q)... It is possible to obtain this behave?

EDIT 1: If I remove static_assert the class works normally.

Matrix<std::complex<double>> m1(3,3);
m1.fill(std::complex<double>(1.,1.));
cout << m1 << endl;
PeeHaa
  • 71,436
  • 58
  • 190
  • 262

2 Answers2

15

The arithmetic in is_arithmetic is a misnomer. Or rather, it's a C++-nomer. It doesn't mean the same thing as it means in English. It just means it's one of the built-in numeric types(int, float, etc...). std::complex is not a built-in, it is a class.

Do you really need that static_assert? Why not just let the user try it with any type? If the type doesn't support the needed operations, then tough luck.

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • Obviously the `static_assert` is not really needed. I used it to "clean" the code and let my matrix be used only for maths... –  Aug 19 '12 at 09:27
  • 2
    @R.M. Why would you want to impose such a restriction? – Benjamin Lindley Aug 19 '12 at 09:29
  • 4
    Anyone who wants to multiply a matrix of std::string deserves what they get. – Jesus Ramos Aug 19 '12 at 09:32
  • 3
    @BenjaminLindley: A pretty big part of the point of `static_assert` is to provide more readable and legitimate error messages for template substitution failures, rather than a giant template instantiation spew. – Nicol Bolas Aug 19 '12 at 09:34
  • @BenjaminLindley Because the class is conceived for maths. Using a matrix of double*, std::string, std::vector, std::exception, ... is a non-sense. –  Aug 19 '12 at 09:36
  • @R.M. You could also check, if `std::numeric_limits::is_specialized` is `true`, since it is guaranteed to be specialized for each buitlin arithmetic type and anybody implementing a user-defined arithmetic type (like `std::complex`, too) should provide a specialization anyway. – Christian Rau Aug 19 '12 at 09:40
  • @R.M. Forget what I said. `std::numeric_limits` only makes sense for scalar types and there is no specialization for `std::complex`, sorry. – Christian Rau Aug 19 '12 at 09:45
  • But IIRC `numeric_limits` is *not* specialized for `std::complex`. Also, I think it would be impossible to properly define its members for multiple precision number types where values like epsilon may depend on the object. – celtschk Aug 19 '12 at 09:46
  • There is not a method to decide what template specialization are possible or not? –  Aug 19 '12 at 09:51
  • @R.M.: I'm assuming in your functions, you use the addition operator and the multiplication operator, yes? If so, none of those types you mentioned will compile, even without the `static_assert`. There is a no way to check if a class really is an arithmetic type, that's just a concept. I say if it walks like a duck and quacks, you may as well treat it as if it's a duck. – Benjamin Lindley Aug 19 '12 at 09:51
  • @BenjaminLindley Yes. I use * and + operators. It is obvious that a std::string will not compile but I thought it was more clean to have a `static_assert` error instead of a long list of errors around the operators. –  Aug 19 '12 at 09:56
  • @R.M.: Okay, so you're after the improved error messages. That's a noble plight. There might be a good solution. My first inclination is to put an assertion for each operation that you need. Though I don't know the syntax for that (or if it's even possible). My experience with `static_assert` is rather limited, but my curiosity is piqued and I will be investigating this over the next few days. – Benjamin Lindley Aug 19 '12 at 10:02
  • You also might want to look into Boost's concept checks. – celtschk Aug 19 '12 at 10:03
1

You could do interesting things with matrices of types that aren't normally considered "numeric". Matrices and vectors actually generalize from numbers to many kinds of "algebraic ring"- basically, any set of objects where the usual + and * operations are defined.

So, you could have matrices of vectors, other matrices, complex numbers, etc. Any classes or primitive types that support add, subtract, and multiply operators would then work correctly. If you define the operators correctly, the implicit compile bombs should catch most abuses like "matrix<std::string>".

Zack Yezek
  • 1,408
  • 20
  • 7