istream_iterator 不进行零初始化
istream_iterator Does Not Zero-Initialize
这是一个Minimal, Complete, Verifiable Example我知道这不是copacetic。无论如何,给定结构:
struct Foo {
int even;
int odd;
};
istream& operator>>(istream& lhs, Foo& rhs) {
int input;
lhs >> input;
(input % 2 == 0 ? rhs.even : rhs.odd) = input;
return lhs;
}
我可以做到以下几点:
stringstream bar("1 2 3 4 5 6 7 8 9 0");
for (const auto& i : vector<Foo>{istream_iterator<Foo>(bar), istream_iterator<Foo>()}) {
cout << i.even << ' ' << i.odd << endl;
}
但是这给了我结果:
-1215720516 1
2 1
2 3
4 3
4 5
6 5
6 7
8 7
8 9
0 9
零初始化 Foo
我可以写代码:
for(Foo i{}; bar >> i; i = Foo{}) {
cout << i.even << ' ' << i.odd << endl;
}
这给出了我预期的结果:
0 1
2 0
0 3
4 0
0 5
6 0
0 7
8 0
0 9
0 0
我知道有一个不完全覆盖变量的提取运算符是粗略的。这最初源于我的回答 here and my question ,在我看来,我更自然地期望在读取之间对变量进行零初始化。在任何情况下,是否可以使用 istream_iterator
使得变量在读取之间被零初始化,或者我必须使用 for
循环?
Is it possible to use an istream_iterator such that the variable is zero-initialized between reads?
如果您查看 internals of the istream_iterator 它具有以下内部状态。
private:
istream_type* _M_stream;
_Tp _M_value;
bool _M_ok;
其中_M_value是默认构造的。
当使用 ++/++ 时,它会调用 _M_read(),它在其实现中具有以下相关行。
*_M_stream >> _M_value;
因此迭代器本身永远不会触及提取器之外的 Foo 的状态,并且 _M_value
在调用之间重复使用。也就是说,您需要自己以某种方式对其进行初始化。我觉得在operator>>
是一个合理的地方。
in my mind had a more natural expectation of zero-initializing the variable in-between reads
这是不正确的期望。 operator>>
应该 完全 和 单独 负责初始化对象。您不能假设该对象以前是 default/value-initialized。一个非常标准的用例是在 while 循环中读取所有对象:
Foo foo;
while (std::cin >> foo) { ... }
第二次通过时,foo
将具有旧值 - 这里没有任何归零。所以你需要确保当你的运算符returns时,新对象完全由你设置。
最简单的就是先对其进行值初始化:
istream& operator>>(istream& lhs, Foo& rhs) {
int input;
lhs >> input;
rhs = Foo{}; // <== add this
input % 2 == 0 ? rhs.even : rhs.odd) = input;
return lhs;
}
或者您可以手动编写两者:
if (input % 2 == 0) {
rhs.odd = 0;
rhs.even = input;
}
else {
rhs.odd = input;
rhs.even = 0;
}
或聚合初始化每个案例:
rhs = input % 2 == 0 ? Foo{input, 0} : Foo{0, input};
无论如何,operator>>
负责将您想要清零的值清零。
这是一个Minimal, Complete, Verifiable Example我知道这不是copacetic。无论如何,给定结构:
struct Foo {
int even;
int odd;
};
istream& operator>>(istream& lhs, Foo& rhs) {
int input;
lhs >> input;
(input % 2 == 0 ? rhs.even : rhs.odd) = input;
return lhs;
}
我可以做到以下几点:
stringstream bar("1 2 3 4 5 6 7 8 9 0");
for (const auto& i : vector<Foo>{istream_iterator<Foo>(bar), istream_iterator<Foo>()}) {
cout << i.even << ' ' << i.odd << endl;
}
但是这给了我结果:
-1215720516 1
2 1
2 3
4 3
4 5
6 5
6 7
8 7
8 9
0 9
零初始化 Foo
我可以写代码:
for(Foo i{}; bar >> i; i = Foo{}) {
cout << i.even << ' ' << i.odd << endl;
}
这给出了我预期的结果:
0 1
2 0
0 3
4 0
0 5
6 0
0 7
8 0
0 9
0 0
我知道有一个不完全覆盖变量的提取运算符是粗略的。这最初源于我的回答 here and my question istream_iterator
使得变量在读取之间被零初始化,或者我必须使用 for
循环?
Is it possible to use an istream_iterator such that the variable is zero-initialized between reads?
如果您查看 internals of the istream_iterator 它具有以下内部状态。
private:
istream_type* _M_stream;
_Tp _M_value;
bool _M_ok;
其中_M_value是默认构造的。
当使用 ++/++ 时,它会调用 _M_read(),它在其实现中具有以下相关行。
*_M_stream >> _M_value;
因此迭代器本身永远不会触及提取器之外的 Foo 的状态,并且 _M_value
在调用之间重复使用。也就是说,您需要自己以某种方式对其进行初始化。我觉得在operator>>
是一个合理的地方。
in my mind had a more natural expectation of zero-initializing the variable in-between reads
这是不正确的期望。 operator>>
应该 完全 和 单独 负责初始化对象。您不能假设该对象以前是 default/value-initialized。一个非常标准的用例是在 while 循环中读取所有对象:
Foo foo;
while (std::cin >> foo) { ... }
第二次通过时,foo
将具有旧值 - 这里没有任何归零。所以你需要确保当你的运算符returns时,新对象完全由你设置。
最简单的就是先对其进行值初始化:
istream& operator>>(istream& lhs, Foo& rhs) {
int input;
lhs >> input;
rhs = Foo{}; // <== add this
input % 2 == 0 ? rhs.even : rhs.odd) = input;
return lhs;
}
或者您可以手动编写两者:
if (input % 2 == 0) {
rhs.odd = 0;
rhs.even = input;
}
else {
rhs.odd = input;
rhs.even = 0;
}
或聚合初始化每个案例:
rhs = input % 2 == 0 ? Foo{input, 0} : Foo{0, input};
无论如何,operator>>
负责将您想要清零的值清零。