保持平等与稳定有什么区别?

时间:2019-09-26 05:11:34

标签: c++ language-lawyer c++-concepts

根据draft

  

如果在给定相等的输入的情况下,表达式导致相等的输出,则该表达式将保持相等性。[...]

  

[...] 稳定:对于具有相同输入对象的这种表达式的两个求值必须具有相等的输出,而无需对这些输入对象进行任何明确的干预。[...] < / p>

强调我。

  • 这些有什么区别?

  • 一个表达式可以保持相等但不能稳定 vice-versa

  • >

2 个答案:

答案 0 :(得分:4)

对于修改其输入的操作,两者将有所不同。

// 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 = f(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;

答案 1 :(得分:1)

批准的答案不正确。 这个功能:

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 变量,尽管这实际上可以实现,我们将具有稳定性,因为我们可以观察到“显式干预修改”。这就是标准提到稳定性禁止“自发”的原因变化,也就是波动性,来自其他执行线程的变化,等等。参见概念.lib.general.equality)

另请注意,相等性保留不依赖于相等性运算符的可用性(并且equality_comparable 概念实际上需要相等性保留)。平等没有明确定义,但应被视为可替代性或不可区分性,标准中经常提到这一点,例如在 strong_ordering 中。

一个函数如果不保持等式就不可能是稳定的,因为稳定性是等式保持的静态概念的动态扩展。