下面的代码片段显示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;
我期待它打印
1 2 3
hi 1 hi 2 hi 3
但是打印
1 3 3
hi 1 hi 3 hi 3
为什么呢?我的编译器是gcc 4.9.3。
我正在尝试将两个向量排序在一起,并在https://stackoverflow.com/a/37929675/3667089中建议使用引用对向量。
答案 0 :(得分:2)
我会说std::pair<int&, std::string&>
不可交换。 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;
然后pair0
覆盖了pair1
。
参考文献通常不会使用STL容器。
答案 1 :(得分:1)
它可能是libstdc ++中的一个错误。我添加了一个&#34; debug&#34;比较器,以查看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;
});
这是输出:
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
由于某种原因,2
元素会被3
覆盖。
另一种可能性是std::pair<int&, std::string&>
不符合ValueSwappable
的定义。也就是说,当在sort函数中发生元素的实际交换时,您将遇到未定义的行为。
答案 2 :(得分:1)
问题在于插入排序实现。由于元素数量较少,因此sort决定执行插入排序。
有问题的代码:
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);
}
现在它进入了索引2和索引3情况的while循环。在进入while循环之前,__val
的值为2 and hi 2
。但在一段时间内完成第一次move
之后,__val
也会更改为3 and hi 3
,而last
之外的3 and hi 3
设置为__sort3
,这是不正确的。< / p>
更新:libc ++
libc ++中的代码非常简单,归结为只比较swap
中的3个值。与move
不同,libc ++使用libstd++
代替moved
,因此有效。
因此,似乎移动引用类型并不好,因为它在为move
元素分配了新值时会覆盖数据。 如果我们使用超过3或4个元素,也可能是libc ++中的结果相同,在这种情况下,它也会在通用插入排序例程下使用 swap
。
更新2:
检查更多内容,libc ++至少对insert_sort使用reference type
,所以没有麻烦。
引用标准为什么引用不好存储在容器中: (第23.2节)
容器是存储其他对象的对象。他们控制着 通过构造函数分配和释放这些对象, 析构函数,插入和擦除操作。
下面的表格有这个要求:
X :: value_type - T需要:T是编译时类型Destructible
虽然我不确定 link.setAttribute('href', entries[i].link);
link.setAttribute('target','_system');
是否可以破坏。如果没有,我们最好在STL容器中存储引用时出现编译时错误。