为什么std :: sort似乎交替了向量中的值

时间:2016-06-20 22:51:31

标签: c++ c++11 vector stl

下面的代码片段显示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中建议使用引用对向量。

3 个答案:

答案 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容器中存储引用时出现编译时错误。