Initialize a matrix like in the Eigen library!

Jan 29, 2020 at 11:20pm
I am planning on writing a Matrix2d class which will be fun for me and help me get a better grasp on matrices in linear algebra.

The first thing I want to do is initialize the matrix and I would like to do it like the Matrix2d from the Eigenlibrary does:

1
2
3
4
5
6
  Matrix2d m;
m << 1, 2, 3,
     4, 5, 6,
     7, 8, 9;

std::cout << m;


How would I would tackle this task?? I know I have to use the operator<< but I don't know how. In fact I only know how use operator<< with ostream.

Thanks.
Last edited on Jan 29, 2020 at 11:22pm
Jan 29, 2020 at 11:35pm
You would need to overload both operator<< and operator,
A simple example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <vector>

class Array {
    std::vector<int> v;
public:
    Array() = default;
    friend std::ostream& operator<<(std::ostream& out, const Array& a) {
        for (int n: a.v) out << n << ' ';
        return out;
    }
    friend Array& operator<<(Array& a, int n) {
        a.v.push_back(n);
        return a;
    }
};

Array& operator,(Array& a, int n) {
    return a << n;
}

int main() {
    Array a;
    a << 1, 2, 3, 4, 5;
    std::cout << a << '\n';
}

Last edited on Jan 29, 2020 at 11:38pm
Jan 30, 2020 at 1:01pm
@dutch

Wow, didn't know exited an operator, in C++.

Thank you very much.
Jan 30, 2020 at 10:23pm
Hey,

The code above works fine inside main.cpp, but when I put the class in a header file it throws an error:

multiple definition of `operator,(Array&, int)'

It says it was first defined in a file 'vector.tcc'.
Jan 30, 2020 at 10:56pm
Seems unlikely, doesn't it.
You need to give more detail.
A complete program that demonstrates the problem, as simple as possible.
What system and compiler are you using?
What is the compile line?
Jan 31, 2020 at 2:55am
The compiler I am using is a MinGW distro version 17.1 thant currently contains GCC 9.2.0 and Boost 1.71.0, downloaded from here https://nuwen.net/mingw.html

My enviroment is codeblocks.

header file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Array {
    std::vector<int> v;
public:
    Array() = default;
    friend std::ostream& operator<<(std::ostream& out, const Array& a) {
        for (int n: a.v) out << n << ' ';
        return out;
    }
    friend Array& operator<<(Array& a, int n) {
        a.v.push_back(n);
        return a;
    }
};

Array& operator,(Array& a, int n) {
    return a << n;
}


 
c:\gcc\mingw\include\c++\9.2.0\bits\vector.tcc|426|multiple definition of `operator,(Array&, int)'; 


line 426 in vector.tcc

Last edited on Jan 31, 2020 at 5:26pm
Jan 31, 2020 at 2:59am
Hey I solved the problem

What I did was declare the function Array& operator,(Array& a, int n) as Array friend function and it worked ok.

header file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 class Array {
    std::vector<int> v;
public:
    Array() = default;
    friend std::ostream& operator<<(std::ostream& out, const Array& a) {
        for (int n: a.v) out << n << ' ';
        return out;
    }
    friend Array& operator<<(Array& a, int n) {
        a.v.push_back(n);
        return a;
    }
     friend Array& operator,(Array& a, int n) {
    return a << n;
}

};
Last edited on Jan 31, 2020 at 2:59am
Jan 31, 2020 at 4:42am
It still makes no sense to me.
Why would operator,(Matrix&, int) conflict with operator,(Array& a, int n)?
And what is something called Matrix doing in the standard library?

Anyway, considering how you fixed it, another possibility might be to leave it outside the class but make it inline, since it doesn't need to be a friend.
Jan 31, 2020 at 11:30am
The problem is also that comma delimiting is OK but the structure is lost by the return at the end of the line.

ie typing in the following for a 2 X 3 matrix:
1,2,3
4,5,6 - is ideal but hard to achieve. For my own purposes I am keen to see a solution. Even adding 2, 3 would be good. Maybe a (albeit messy) constructor that asks for the next line/row?
Jan 31, 2020 at 5:43pm
@dutch

Sorry, The error line is wrong, I have edited the post.

the correct error line is this one:

c:\gcc\mingw\include\c++\9.2.0\bits\vector.tcc|426|multiple definition of `operator,(Array&, int)'

operator,(Array&, int) is not from the STL it is in in the header file.

The line in vector.tcc is as follows:

1
2
void vector<_Tp, _Alloc>::
      _M_realloc_insert(iterator __position, _Args&&... __args)
Jan 31, 2020 at 8:09pm
is not from the STL it is in in the header file.

But surely vector.tcc is defining the std::vector class for the standard library. The line you show has no relation to operator, or Array or anything. None of this makes sense to me. And I can't recreate the problem (although you haven't shown the complete program that you are trying to compile).
Jan 31, 2020 at 9:03pm
It doesn't make sense to me neither.

here is the full program.

header.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <vector>
#include <fstream>

class Array
{
    std::vector<int> v;
public:
    Array() = default;
    friend std::ostream& operator<<(std::ostream& out, const Array& a)
    {
        for (int n: a.v) out << n << ' ';
        return out;
    }
    friend Array& operator<<(Array& a, int n)
    {
        a.v.push_back(n);
        return a;
    }
};

Array& operator,(Array& a, int n)
{
    return a << n;
}


main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include "header.h"

using namespace std;

int main()
{
    Array a;

    a << 1, 2, 3, 4, 5;

    cout << a;
}
Jan 31, 2020 at 9:31pm
I can't recreate the problem. Just one last test and I'll let it go. I was hoping someone like JLBorges might have an idea as to what's happening.

Try the following for header.h. I think the important change that might fix the problem (as making the function an in-class friend did) is adding inline. I also added include guards since every header file should have those (or #pragma once ).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ifndef ARRAY_H_     // include guards
#define ARRAY_H_

#include <vector>
#include <ostream>   // used to be fstream

class Array
{
    std::vector<int> v;
public:
    Array() = default;
    friend std::ostream& operator<<(std::ostream& out, const Array& a)
    {
        for (int n: a.v) out << n << ' ';
        return out;
    }
    friend Array& operator<<(Array& a, int n)
    {
        a.v.push_back(n);
        return a;
    }
};

inline Array& operator,(Array& a, int n)   // added inline
{
    return a << n;
}

#endif 

Feb 1, 2020 at 3:32am
Feb 2, 2020 at 5:49am
It seems that overloaded comma initializer's are not good form. Some write they are/can be unreliable and point to the std::initializer_list as a reliable alternative.

So, on that note:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <vector>

int main()
{
    std::initializer_list<int> init_list;
    
    init_list =
    {
        1,2,3,
        4,5,6,
        7,8,9
    };
    
    std::vector<int> vec{init_list};
    
    for (auto it:vec)
        std::cout << it << ' ';
    
    std::cout << '\n';
}


1 2 3 4 5 6 7 8 9 
Program ended with exit code: 0
Last edited on Feb 2, 2020 at 5:50am
Feb 2, 2020 at 6:50am
Gets close, 'there' with a bit of fine tuning.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <vector>
#include <valarray> // <array> DOESN'T ACCEPT AN INITIALIZER LIST

class Matrix
{
public:
    int m_rows;
    int m_cols;
    
    std::vector<double> m_vec;
public:
    Matrix(int r, int c){m_rows = r; m_cols = c;}
    Matrix(int r, int c, std::initializer_list<double> list)
    {
        m_rows = r; m_cols = c; // CHECK HERE FOR SIZE OF LIST
        m_vec.clear();
        for(auto it:list)       // 2D vs 1D VECTOR
            m_vec.push_back(it);
    }
    
    ~Matrix(){};
    
    friend std::ostream& operator<< (std::ostream& out, Matrix& m)
    {
        for(auto iter :m.m_vec)
            out << iter << ' ';
        out << '\n';
        
        return out;
    }
    
};

int main()
{
    std::initializer_list<int> init_list;
    
    init_list =
    {
        1,2,3,
        4,5,6,
        7,8,9
    };
    
    std::cout << "VECTOR\n";
    std::vector<int> vec{init_list};
    for (auto it:vec)
        std::cout << it << ' ';
    std::cout << "\n\n";
    
    std::cout << "VALARRAY\n";
    std::valarray<int> arr(init_list);
    for (auto it:arr)
        std::cout << it << ' ';
    std::cout << "\n\n";
    
    std::cout << "MATRIX\n";
    Matrix m(3,3,  // PROBABLY CAN AVOID THIS BY INITIALLY DECLARING MATRIX(r,c)
    {
        1,2,3,
        4,5,6,
        7,8,9
    } );
    
    std::cout << m;
    
    return 0;
}
Feb 2, 2020 at 9:51am
Thank you @dutch for all, programming community appreciates people like you.

@againtry, std::initializer_list seems to be a nice option, I will be sure to check it out. Thank you
Feb 5, 2020 at 5:39pm
Topic archived. No new replies allowed.