我有以下代码,我写的是为了测试更大程序的一部分:
#include <fstream>
#include <random>
#include <iostream>
using namespace std ;
int main()
{
mt19937_64 Generator(12187) ;
mt19937_64 Generator2(12187) ;
uniform_int_distribution<int> D1(1,6) ;
cout << D1(Generator) << " " ;
cout << D1(Generator) << " " << D1(Generator) << endl ;
cout << D1(Generator2) << " " << D1(Generator2) << " " << D1(Generator2) << endl ;
ofstream g1("g1.dat") ;
g1 << Generator ;
g1.close() ;
ofstream g2("g2.dat") ;
g2 << Generator2 ;
g2.close() ;
}
两个生成器的种子具有相同的值,因此我希望输出中的第二行与第一行相同。相反,输出是
1 1 3
1 3 1
*.dat
文件中打印的两个生成器的状态是相同的。我想知道在随机数生成中是否存在一些隐藏的多线程导致订单不匹配。
我使用g++
版本5.3.0在Linux上编译,标记为-std=c++11
。
提前感谢您的帮助。
答案 0 :(得分:42)
x << y
是函数调用operator<<(x, y)
的语法糖。
你会记得c ++标准对函数调用的参数的评估顺序没有限制。
因此,编译器可以自由地发出首先计算x或y的代码。
从标准:§5注2:
操作符可以重载,也就是说,当应用于类类型的表达式时给定含义(Clause 9)或枚举类型(7.2)。 如上所述,重载运算符的使用将转换为函数调用 在13.5 。重载运算符遵循第5章中规定的语法规则,但要求符合 操作数类型,值类别,和评估顺序将被函数调用规则替换。
答案 1 :(得分:36)
这是因为此行的评估顺序
cout << D1(Generator2) << " " << D1(Generator2) << " " << D1(Generator2) << endl ;
不是你的想法。
你可以用这个来测试它:
int f() {
static int i = 0;
return i++;
}
int main() {
cout << f() << " " << f() << " " << f() << endl ;
return 0;
}
输出:2 1 0
订单未由C ++标准指定,因此订单在其他编译器上可能有所不同,请参阅Richard Hodges&#39;答案。
答案 2 :(得分:5)
对程序稍作修改会发现会发生什么:
#include <fstream>
#include <random>
#include <iostream>
using namespace std ;
int main()
{
mt19937_64 Generator(12187) ;
mt19937_64 Generator2(12187) ;
uniform_int_distribution<int> D1(1,100) ;
cout << D1(Generator) << " " ;
cout << D1(Generator) << " " ;
cout << D1(Generator) << endl ;
cout << D1(Generator2) << " " << D1(Generator2) << " " << D1(Generator2) << endl ;
}
输出:
4 48 12
12 48 4
所以你的生成器会产生相同的结果 - 但你的cout-line参数的顺序是按不同的顺序计算的。
在线试用: http://ideone.com/rsoqDe
答案 3 :(得分:4)
这些行
public static void printSchoneAutos(ArrayList<Auto> autos) {
Iterator<Auto> iter = autos.iterator();
while (iter.hasNext()) {
Auto auto = iter.next();
if (auto instanceof BrandstofAuto) {
boolean isschoon = ((BrandstofAuto) auto).isSchoon();
boolean isschoon3 = auto.isSchoon();
if (isschoon3 == false || isschoon == false) {
iter.remove();
}
}
for (int i = 0; i < autos.size(); i++) {
System.out.println(autos.get(i));
}
}
}
因为 cout << D1(Generator) << " " ;
cout << D1(Generator) << " "
<< D1(Generator) << endl ;
cout << D1(Generator2) << " "
<< D1(Generator2) << " "
<< D1(Generator2) << endl ;
返回一个int D1()
有一个重载,正在有效调用(不包括ostream::operator<<()
)
endl
现在,标准就是这样说的,
§5.2.2[4]
调用函数时,每个参数都应该 用相应的参数初始化。
[注意:此类初始化相对于彼此不确定地排序 - 结束语]
如果该功能是非静态的 成员函数,该函数的该参数应为 用指向调用对象的指针初始化
所以让我们分解前面的表达式
cout.operator<<(D1(Generator));
cout.operator<<(D1(Generator))
.operator<<(D1(Generator));
cout.operator<<(D1(Generator2))
.operator<<(D1(Generator2))
.operator<<(D1(Generator2));
为了说明cout.operator<<(a()) // #1
.operator<<(b()) // #2
.operator<<(c()); // #3
指针的构造,这些在概念上等同于(为了简洁而省略this
):
ostream::
现在让我们来看看顶级电话。我们首先评估哪个,operator<<( // #1
&operator<<( // #2
&operator<<( // #3
&cout,
a()
), // end #3
b()
), // end #2
c()
); // end #1
或#2
?因为正如引文中强调的那样,顺序是不确定的,那么我们不知道 - 这是递归的:即使我们评估c()
,我们仍然会面临是否评估其内部{{}的问题。 1}}或#2
。
所以希望能更清楚地解释这里发生的事情。