[英]Why does std::sort appears to alternate the values in the vector
The below code snippet shows that std::sort is alternating the values in the vector which is quite confusing. 下面的代码片段显示std :: sort正在交替向量中的值,这非常令人困惑。
std::vector<int> a;
std::vector<std::string> b;
std::vector<std::pair<int&, std::string&>> p;
a.push_back(1);
a.push_back(3);
a.push_back(2);
b.push_back("hi 1");
b.push_back("hi 3");
b.push_back("hi 2");
p.push_back(std::pair<int&, std::string&>(a[0],b[0]));
p.push_back(std::pair<int&, std::string&>(a[1],b[1]));
p.push_back(std::pair<int&, std::string&>(a[2],b[2]));
std::sort(p.begin(),p.end());
std::cout << a[0] << " " << a[1] << " " << a[2] << std::endl;
std::cout << b[0] << " " << b[1] << " " << b[2] << std::endl;
I am expecting it to print 我希望它能打印
1 2 3
hi 1 hi 2 hi 3
but instead it prints 但它打印
1 3 3
hi 1 hi 3 hi 3
Why? 为什么? My compiler is gcc 4.9.3. 我的编译器是gcc 4.9.3。
I am trying to sort two vectors together and using vector of pairs of references is suggested in https://stackoverflow.com/a/37929675/3667089 . 我试图将两个向量排序在一起,并在https://stackoverflow.com/a/37929675/3667089中建议使用成对的引用向量。
I would say std::pair<int&, std::string&>
is not swappable. 我会说std::pair<int&, std::string&>
是不可交换的。 std::swap
seems to work though, but if you think in a naive swap, it won't work with references std::swap
似乎可以工作,但是如果您认为是幼稚的交换,它将不能与引用一起工作
std::pair<int&, std::string&> pair0(a[0],b[0]);
std::pair<int&, std::string&> pair1(a[1],b[1]);
std::pair<int&, std::string&> tmp(pair0);
pair0 = pair1;
pair1 = tmp;
then pair0
gets overwritten by pair1
. 然后pair0
得到由覆盖pair1
。
References in general won't work with STL containers. 通常,引用不适用于STL容器。
It's possibly a bug in libstdc++. 这可能是libstdc ++中的错误。 I added a "debug" comparator to see the sort in action for both libstdc++ and libc++: 我添加了一个“调试”比较器,以查看libstdc ++和libc ++的排序方式:
std::sort(p.begin(), p.end(), [&] (const auto& ap, const auto& bp)
{
std::cout << "Debug: " << ap.first << " " << bp.first << " | ";
for (auto e : a)
std::cout << e << " ";
std::cout << " | ";
for (auto e : b)
std::cout << e << " ";
std::cout << "\n";
return ap.first < bp.first;
});
And this is the output: 这是输出:
Debug: 1 1 | 1 3 2 | hi 1 hi 3 hi 2
Debug: 3 1 | 1 3 2 | hi 1 hi 3 hi 2
Debug: 3 1 | 1 3 2 | hi 1 hi 3 hi 2
Debug: 2 1 | 1 3 2 | hi 1 hi 3 hi 2
Debug: 2 3 | 1 3 2 | hi 1 hi 3 hi 2
Debug: 3 1 | 1 3 3 | hi 1 hi 3 hi 3
1 3 3
hi 1 hi 3 hi 3
--------------------------
Debug: 3 1 | 1 3 2 | hi 1 hi 3 hi 2
Debug: 2 3 | 1 3 2 | hi 1 hi 3 hi 2
Debug: 2 1 | 1 2 3 | hi 1 hi 2 hi 3
1 2 3
hi 1 hi 2 hi 3
For some reason the 2
element gets overwritten by a 3
. 由于某些原因, 2
元素将被3
覆盖。
The other possibility is that std::pair<int&, std::string&>
does not meet the definition of ValueSwappable
. 另一种可能性是std::pair<int&, std::string&>
不符合ValueSwappable
的定义。 That is, when the actual swapping of elements occur in the sort function, you are experiencing undefined behavior. 也就是说,当实际的元素交换发生在sort函数中时,您将遇到未定义的行为。
The problem is in the insertion sort implementation. 问题出在插入排序实现中。 Since there are less number of elements, sort decides to perform an insertion sort. 由于元素数量较少,sort决定执行插入排序。
Problematic code: 有问题的代码:
template<typename _RandomAccessIterator>
void
__unguarded_linear_insert(_RandomAccessIterator __last)
{
typename iterator_traits<_RandomAccessIterator>::value_type
__val = _GLIBCXX_MOVE(*__last);
_RandomAccessIterator __next = __last;
--__next;
while (__val < *__next)
{
*__last = _GLIBCXX_MOVE(*__next); <<< PROBLEMATIC Code
__last = __next;
--__next;
}
*__last = _GLIBCXX_MOVE(__val);
}
Now it goes inside the while loop for the index 2 and index 3 case. 现在它进入索引2和索引3情况的while循环中。 Before going into the while loop, __val
has the value 2 and hi 2
. 在进入while循环之前, __val
的值为2 and hi 2
。 But after doing the first move
inside the while, __val
also changes to 3 and hi 3
and outside the while last
is set to 3 and hi 3
, which is incorrect. 但这样做后的第一个move
的同时里面, __val
也变为3 and hi 3
和外面时last
设置为3 and hi 3
,这是不正确。
UPDATE: libc++ Code in libc++ for this is pretty straightforward and boils down to comparing just 3 values in __sort3
. 更新:libc ++为此, libc ++中的代码非常简单,归结为仅比较__sort3
3个值。 libc++ uses swap
instead of move
unlike libstd++
and hence works. libc ++与libstd++
不同, libstd++
使用swap
而不是move
,因此可以工作。
So, seems like moving on reference types is not good as it overwrites the data when the moved
element is assigned with new value. 因此,在引用类型上移动似乎不好,因为在为moved
元素分配新值时它会覆盖数据。
Perhaps the same would be the result in libc++ also if we use more than 3 or 4 elements in which case it will also use
move
under a generic insertion sort routine.
如果我们使用3个或4个以上的元素,libc ++中的结果也可能相同,在这种情况下,它还将在通用插入排序例程下使用
move
。
UPDATE 2: Checking more into it, libc++ always uses swap
at least for insertion_sort, so no trouble there. 更新2:对其进行更多检查,libc ++至少始终对swapping_sort使用swap
,因此在那里没有麻烦。
To quote from standard on why references are not good to be stored in container: (Section 23.2) 用标准引述为什么引用不能很好地存储在容器中:(第23.2节)
Containers are objects that store other objects. 容器是存储其他对象的对象。 They control allocation and deallocation of these objects through constructors, destructors, insert and erase operations. 它们通过构造函数,析构函数,插入和擦除操作来控制这些对象的分配和释放。
And the table beneath that has this requirement: 而下面的表有此要求:
X::value_type - T Requires: T is compile time type Destructible X :: value_type-T要求:T是编译时类型可破坏的
Though I am not sure if reference type
is destructible or not. 虽然我不确定reference type
是否可破坏。 If not, we should better get compile time errors for storing references in STL containers. 如果不是这样,我们最好在将引用存储在STL容器中时出现编译时错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.