应该 set::equal_range() return 对 ( set::lower_bound(), set::upper_bound() )
Should set::equal_range() return pair( set::lower_bound(), set::upper_bound() )
是的,我知道,我应该有一个复制品,但制作复制品很烦人。在我制作一个(并且可能给 clang 一个错误)之前,我想确保我对事物应该如何工作的理解是正确的。
MSVC: Returns set::equal_range()
作为 {lower_bound(),upper_bound()}
.
叮当声:Returns set::equal_range()
为 {lower_bound(),upper_bound()-1}
。 (使用 stdlib=libc++)——当然还有我的例子。
我对过去 20 年的 STL 编程的期望是 MSVC 是正确的。事实上,我正在编辑的代码是我 20 年前写的,它曾经在 clang 和 gcc 中工作,但现在只在 MSVC 中工作(也许是 gcc - 我没试过 - 我正在使用一些 headers 是 C++20,仅在 stdlib=libc++ 和 MSVC 中,但不在 gcc 中)。
无论如何,我关于 set::equal_range()
应该 return 的假设正确吗?
好吧,看看吧:
Clang with -stdlib=libc++: https://godbolt.org/z/xfnasKa73
没有 -stdlib=libc++ 的 Clang:https://godbolt.org/z/5qYEj4MEd
如果您认为我的比较器是假的,请告诉我。
测试代码如下:
#include <iostream>
#include <utility>
#include <set>
using namespace std;
struct Range : public pair< size_t, size_t >
{
using pair::pair;
bool operator < ( Range const & _r ) const
{
return second < _r.first;
}
};
typedef set< Range > vTySet;
int
main()
{
vTySet setRanges = {{0,0},{9,9},{10,10},{13,13},{32,32},{34,34},{61,61},{65,90},{101,101},{110,110}};
Range rngTest = { 97, 122 };
pair< vTySet::iterator, vTySet::iterator > pritEqualRange = setRanges.equal_range( rngTest );
size_t nDistEqualRange = distance( pritEqualRange.first, pritEqualRange.second );
pair< vTySet::iterator, vTySet::iterator > pritLowerUpper = { setRanges.lower_bound( rngTest ), setRanges.upper_bound( rngTest ) };
size_t nDistLowerUpper = distance( pritLowerUpper.first, pritLowerUpper.second );
cout << "nDistEqualRange:" << nDistEqualRange << "\n";
cout << "nDistLowerUpper:" << nDistLowerUpper << "\n";
}
任何用于 std::set
的自定义比较器都应满足 Compare 要求(严格弱排序 + 等价)。您的代码中有 UB,您看到的差异是该 UB 的结果。在您的示例中,元素是 (a, b)
对,比较器(我们称之为 less
)是 (a, b) < (c, d)
iff b < c
.
考虑两对 (5, 2)
和 (6, 3)
。不对称性 属性 要求如果 (5, 2) < (6, 3)
为真,则 (6, 3) < (5, 2)
应该为假。但这也是事实:
Range r1(5, 2);
Range r2(6, 3);
std::cout << less(r1, r2); // 1
std::cout << less(r2, r1); // 1, but should be 0
等价关系的传递性也被这样的比较器打破了:
Range r1(1, 3);
Range r2(2, 5);
Range r3(4, 6);
std::cout << equiv(r1, r2); // 1
std::cout << equiv(r2, r3); // 1
std::cout << equiv(r1, r3); // 0, but should be 1
需要注意的是,这些要求不仅适用于集合中的元素,还适用于作为键传递给 std::set::equal_range(const Key&)
的元素。
是的,我知道,我应该有一个复制品,但制作复制品很烦人。在我制作一个(并且可能给 clang 一个错误)之前,我想确保我对事物应该如何工作的理解是正确的。
MSVC: Returns
set::equal_range()
作为{lower_bound(),upper_bound()}
.叮当声:Returns
set::equal_range()
为{lower_bound(),upper_bound()-1}
。 (使用 stdlib=libc++)——当然还有我的例子。
我对过去 20 年的 STL 编程的期望是 MSVC 是正确的。事实上,我正在编辑的代码是我 20 年前写的,它曾经在 clang 和 gcc 中工作,但现在只在 MSVC 中工作(也许是 gcc - 我没试过 - 我正在使用一些 headers 是 C++20,仅在 stdlib=libc++ 和 MSVC 中,但不在 gcc 中)。
无论如何,我关于 set::equal_range()
应该 return 的假设正确吗?
好吧,看看吧:
Clang with -stdlib=libc++: https://godbolt.org/z/xfnasKa73
没有 -stdlib=libc++ 的 Clang:https://godbolt.org/z/5qYEj4MEd
如果您认为我的比较器是假的,请告诉我。
测试代码如下:
#include <iostream>
#include <utility>
#include <set>
using namespace std;
struct Range : public pair< size_t, size_t >
{
using pair::pair;
bool operator < ( Range const & _r ) const
{
return second < _r.first;
}
};
typedef set< Range > vTySet;
int
main()
{
vTySet setRanges = {{0,0},{9,9},{10,10},{13,13},{32,32},{34,34},{61,61},{65,90},{101,101},{110,110}};
Range rngTest = { 97, 122 };
pair< vTySet::iterator, vTySet::iterator > pritEqualRange = setRanges.equal_range( rngTest );
size_t nDistEqualRange = distance( pritEqualRange.first, pritEqualRange.second );
pair< vTySet::iterator, vTySet::iterator > pritLowerUpper = { setRanges.lower_bound( rngTest ), setRanges.upper_bound( rngTest ) };
size_t nDistLowerUpper = distance( pritLowerUpper.first, pritLowerUpper.second );
cout << "nDistEqualRange:" << nDistEqualRange << "\n";
cout << "nDistLowerUpper:" << nDistLowerUpper << "\n";
}
任何用于 std::set
的自定义比较器都应满足 Compare 要求(严格弱排序 + 等价)。您的代码中有 UB,您看到的差异是该 UB 的结果。在您的示例中,元素是 (a, b)
对,比较器(我们称之为 less
)是 (a, b) < (c, d)
iff b < c
.
考虑两对 (5, 2)
和 (6, 3)
。不对称性 属性 要求如果 (5, 2) < (6, 3)
为真,则 (6, 3) < (5, 2)
应该为假。但这也是事实:
Range r1(5, 2);
Range r2(6, 3);
std::cout << less(r1, r2); // 1
std::cout << less(r2, r1); // 1, but should be 0
等价关系的传递性也被这样的比较器打破了:
Range r1(1, 3);
Range r2(2, 5);
Range r3(4, 6);
std::cout << equiv(r1, r2); // 1
std::cout << equiv(r2, r3); // 1
std::cout << equiv(r1, r3); // 0, but should be 1
需要注意的是,这些要求不仅适用于集合中的元素,还适用于作为键传递给 std::set::equal_range(const Key&)
的元素。