如何在 C++ 中实现以逗号分隔的初始化,例如 Eigen 中的初始化?

How could comma separated initialization such as in Eigen be possibly implemented in C++?

这是 Eigen 文档的一部分:

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

输出:

1 2 3
4 5 6
7 8 9

我不明白上面的 operator<< 怎么能捕获所有逗号分隔的值。我做了一个小实验:

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

可以预见(根据我对 C++ 语法的理解)只有一个值被 operator<< 捕获:

Just commas: 1
Commas in parentheses: 5

故题题。

逗号本身是 c++ 中的一个运算符,它可以被重载(并且显然是通过 eigen 重载的)。我不知道 eigen 实现重载的确切方式,但我相信你可以搜索 eigen 的来源来查找它。

对于你的小经验,你必须了解 un 重载逗号运算符在 c++ 中的工作方式。

逗号运算符的形式为 <statement>,<statement> 并且计算结果为 second 语句计算结果。运算符 << 的优先级高于运算符 ,。因此 cout 在评估其余逗号操作之前先评估。

因为 , 是从左到右关联的,所以代码 (1,2,3,4,5) 等于 ((((1,2),3),4),5),它的计算结果是最右边的值,即 5

基本思想是重载 <<, 运算符。

m << 1 被重载以将 1 放入 m,然后 return 是一个特殊的代理对象——称之为 p——持有对 m.

然后p, 2重载将2放入m和returnp,这样p, 2, 3会先把2 变成 m 然后 3.

Boost.Assign 使用了类似的技术,尽管他们使用 += 而不是 <<

这是一个可能的简化实现

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);
    }
};

想法是 << returns 一个等待第二个元素的 Loader 实例,每个加载器实例使用逗号运算符更新矩阵和 returns另一个加载程序实例。

请注意,重载逗号运算符通常被认为是一个坏主意,因为该运算符最具体的特征是严格的从左到右的评估顺序。然而,当过载时,这并不能保证,例如在

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

g() 可能会在 f() 之前被调用。

请注意,我说的是求值顺序,而不是关联性或优先级,它们当然也适用于重载版本。 例如 g() 可以在 f() 之前调用,但是 f() 的结果保证在 g().

的结果之前正确放置在矩阵中