保持平等和稳定有什么区别?
What is the difference between equality-preserving and stable?
根据 draft:
An expression is equality-preserving if, given equal inputs, the expression results in equal outputs.[...]
和
[...]stable: two evaluations of such an expression with the same input objects are required to have equal outputs absent any explicit intervening modification of those input objects.[...]
强调我的。
这些有什么区别?
当一个表达式可以等式保持但不是稳定并且反之亦然?
对于修改其输入的操作,两者会有所不同。
// stable and equality preserving
int foo(int a, int b) {
return a + b;
}
// equality preserving, but not stable:
int bar(int a, int &b) {
auto ret = a + b;
++b;
return ret;
}
例如:
int x = 1, y = 2;
int z = foo(x, y); // produces 3
int z2 = foo(x, y); // still produces 3
int zz = bar(x, y); // produces 3
int zz2 = bar(x, y); // produces 4
至于稳定但不保持相等的东西,是的,这也是可能的(对于“相等”的某些定义)。
举一个简单的例子,考虑这样的事情:
struct foo {
int bar;
// they're all equal, just some are more equal than others
bool operator==(foo const &) const { return true; }
};
int operator+(foo a, foo b) { return a.bar + b.bar; }
foo a{1};
foo b{2};
foo c{3};
// obviously both true
assert(a == b);
assert(b == c);
int x = a + b;
int y = b + c;
assert(x != y); // of course 1 + 2 != 2 + 3;
批准的答案不正确。
这个函数:
int foo(int a, int b) {
return a + b;
}
确实是守恒稳定的。
但是这个功能也是如此:
int bar(int a, int &b) {
auto ret = a + b;
++b;
return ret;
}
两者的实际区别在于 foo 是正则的(参见概念 regular_invocable)而 bar 不是。
一个保持相等但不稳定的函数的例子是:
int b = 0; // assume accessible by other threads
int zip(int a) {
auto ret = a + b;
++b;
return ret;
}
它是等式保持的,因为等式保持纯粹是静态的,并且在任何给定时间,zip 确实会用相同的输入给出相同的结果。
但是,zip 不稳定,因为稳定性是 动态的,并且在不同的时间,zip 可能 对相同的输入给出不同的结果.
(如果全局变量 b 在两次调用之间没有变化,那么它会给出不同的结果。但是如果另一个线程递减 b,并且没有发生其他变化,它会给出相同的结果。)
(如果没有其他线程可以访问 b 变量,不管这实际上是如何实现的,我们都会有稳定性,因为我们可以观察到“显式干预修改”。这就是标准提到稳定性禁止的原因“自发的”变化,又名波动性,来自其他执行线程的变化,等等。Cf concepts.lib.general.equality)
另请注意,相等性保持不依赖于相等运算符的可用性(equality_comparable 概念实际上需要相等性保持)。相等性没有明确定义,但应该被认为是可替代性,或不可区分性,标准中经常提到,例如strong_ordering.
函数不保持相等就不可能稳定,因为稳定性是静态保持等式概念的动态扩展。
根据 draft:
An expression is equality-preserving if, given equal inputs, the expression results in equal outputs.[...]
和
[...]stable: two evaluations of such an expression with the same input objects are required to have equal outputs absent any explicit intervening modification of those input objects.[...]
强调我的。
这些有什么区别?
当一个表达式可以等式保持但不是稳定并且反之亦然?
对于修改其输入的操作,两者会有所不同。
// stable and equality preserving
int foo(int a, int b) {
return a + b;
}
// equality preserving, but not stable:
int bar(int a, int &b) {
auto ret = a + b;
++b;
return ret;
}
例如:
int x = 1, y = 2;
int z = foo(x, y); // produces 3
int z2 = foo(x, y); // still produces 3
int zz = bar(x, y); // produces 3
int zz2 = bar(x, y); // produces 4
至于稳定但不保持相等的东西,是的,这也是可能的(对于“相等”的某些定义)。
举一个简单的例子,考虑这样的事情:
struct foo {
int bar;
// they're all equal, just some are more equal than others
bool operator==(foo const &) const { return true; }
};
int operator+(foo a, foo b) { return a.bar + b.bar; }
foo a{1};
foo b{2};
foo c{3};
// obviously both true
assert(a == b);
assert(b == c);
int x = a + b;
int y = b + c;
assert(x != y); // of course 1 + 2 != 2 + 3;
批准的答案不正确。 这个函数:
int foo(int a, int b) {
return a + b;
}
确实是守恒稳定的。 但是这个功能也是如此:
int bar(int a, int &b) {
auto ret = a + b;
++b;
return ret;
}
两者的实际区别在于 foo 是正则的(参见概念 regular_invocable)而 bar 不是。
一个保持相等但不稳定的函数的例子是:
int b = 0; // assume accessible by other threads
int zip(int a) {
auto ret = a + b;
++b;
return ret;
}
它是等式保持的,因为等式保持纯粹是静态的,并且在任何给定时间,zip 确实会用相同的输入给出相同的结果。
但是,zip 不稳定,因为稳定性是 动态的,并且在不同的时间,zip 可能 对相同的输入给出不同的结果.
(如果全局变量 b 在两次调用之间没有变化,那么它会给出不同的结果。但是如果另一个线程递减 b,并且没有发生其他变化,它会给出相同的结果。)
(如果没有其他线程可以访问 b 变量,不管这实际上是如何实现的,我们都会有稳定性,因为我们可以观察到“显式干预修改”。这就是标准提到稳定性禁止的原因“自发的”变化,又名波动性,来自其他执行线程的变化,等等。Cf concepts.lib.general.equality)
另请注意,相等性保持不依赖于相等运算符的可用性(equality_comparable 概念实际上需要相等性保持)。相等性没有明确定义,但应该被认为是可替代性,或不可区分性,标准中经常提到,例如strong_ordering.
函数不保持相等就不可能稳定,因为稳定性是静态保持等式概念的动态扩展。