How could comma separated initialization such as i

2020-01-28 09:34发布

问题:

Here's a part of Eigen documentation:

Matrix3f m;
m << 1, 2, 3,
     4, 5, 6,
     7, 8, 9;
std::cout << m;

Output:

1 2 3
4 5 6
7 8 9

I couldn't understand how could all the comma separated values be captured by operator<< above. I did a tiny experiment:

cout << "Just commas: ";
cout << 1, 2, 3, 4, 5;
cout << endl;
cout << "Commas in parentheses: ";
cout << ( 1, 2, 3, 4, 5 );
cout << endl;

Predictably (according to my understanding of C++ syntax) only one of the values was captured by operator<< :

Just commas: 1
Commas in parentheses: 5

Thus the title question.

回答1:

The basic idea is to overload both the << and the , operators.

m << 1 is overloaded to put 1 into m and then returns a special proxy object – call it p – holding a reference to m.

Then p, 2 is overloaded to put 2 into m and return p, so that p, 2, 3 will first put 2 into m and then 3.

A similar technique is used with Boost.Assign, though they use += rather than <<.



回答2:

This is a possible simplified implementation

struct M3f {
    double m[3][3];
    struct Loader {
        M3f& m;
        int i;
        Loader(M3f& m, int i) : m(m), i(i) {}
        Loader operator , (double x) {
            m.m[i/3][i%3] = x;
            return Loader(m, i+1);
        }
    };
    Loader operator<<(double x) {
        m[0][0] = x;
        return Loader(*this, 1);
    }
};

The idea is that << returns a Loader instance that waits for second element, and each loader instance uses the comma operator to update the matrix and returns another loader instance.

Note that overloading the comma operator is generally considered a bad idea because the most specific characteristic of the operator is strict left-to-right evaluation order. However when overloaded this is not guaranteed and for example in

m << f(), g(), ...

g() could end up being called before f().

Note that I'm talking about the evaluation order, not about associativity or precedence that are of course maintained also for overloaded versions. For example g() could be called before f() but the result from f() is guaranteed to be correctly placed in the matrix before the result from g().



回答3:

The comma itself is an operator in c++ which can be overloaded (and apparently is by eigen). I don't know the exact way eigen implements the overloading but I am sure that you can search for the sources of eigen to look it up.

To your little experient you have to understand how the unoverloaded comma operator works in c++.

The comma operator has the form <statement>,<statement> and is evaluated to whatever the second statement is evaluated to. The operator << has a higher precedence than the operator ,. Because of that the cout is evaluated before the rest of the comma operations are evaluated.

Because , is Left-to-right associative the code (1,2,3,4,5) is equal to ((((1,2),3),4),5) which evaluates to the most right value, which is 5.