为什么std :: vector的相同成员的地址在std :: sort的比较函数中是不同的?

时间:2014-06-24 11:46:54

标签: c++ sorting c++11 stl lambda

我想知道为什么这段代码会以不同的方式打印相同的成员:

template <typename FI>
void sorta(FI begin, FI end){
    using T = typename std::iterator_traits<FI>::value_type;
    std::sort(begin, end, [](const T& lhs,const T& rhs){
         std::cout << lhs<<"**"<<&lhs  << '\n';
         return std::rand()%2;
    });
}

int main()
{
    std::vector<int> a{1,5,3,4,7};
    sorta(a.begin(),a.end());
}

live

可能的输出:

5**0x25e5014
3**0x25e5018
3**0x7fff93d5d3e0
...

正如您所看到的,变量3的地址在每个函数调用中都是不同的!

看起来很奇怪我尝试了一个简单的函数,它做了类似于std :: sort的事情:

using Iterator=std::vector<int>::iterator;
template<class Func>
void foo(Iterator begin,Iterator end,Func f)
{
    for(Iterator it=begin; it != end-1;++it)
    {
         auto f2=[&begin,&f](int& lhs,int& rhs){
            std::cout<<&(*begin)<<"\n"<<&lhs<<"  "<<&rhs;
            f(lhs,rhs);
        };

         f2(*it,*(it+1));
    }
}

int main()
{
    std::vector<int> a{1,5,3,4,7};

    foo(a.begin(),a.end(),[](int& lhs,int& rhs){
        std::cout<<"\n"<<&lhs<<"  "<<&rhs<<"\n\n\n";
    });
}

live

可能的输出:

0x837a008
0x837a008  0x837a00c
0x837a008  0x837a00c

0x837a008
0x837a00c  0x837a010
0x837a00c  0x837a010

...

惊喜!

在这种情况下地址是相同的,所以我希望std :: sort做同样的事情!!

第一个代码有什么问题? std :: sort复制变量多次?!

用例:

我试图编写一个返回源索引的排序函数,但由于我解释的问题它没有工作

template <typename FI ,class L >
std::vector<int> sorta(FI begin, FI end, L comp_proc){
    size_t size = std::distance(begin, end);
    using T = typename std::iterator_traits<FI>::value_type;

    std::vector <int> indexs(size);
    for (size_t i = 0; i < indexs.size(); i++)
        indexs[i] = i;

    std::sort(begin, end, [&comp_proc,&begin,&indexs](const T& l_item,const T& r_item){

        size_t l_dis = std::distance(&(*begin),const_cast<T*>(&l_item));//not working correctly
        size_t r_dis = std::distance(&(*begin),const_cast<T*>(&r_item));//not working correctly
        std::cout << l_item<<"**"<<&l_item <<"**"<<l_dis << "**" << r_dis << std::endl;

        bool res = comp_proc(l_item, r_item);
        if (const_cast<T*>(&l_item) > &(*begin) && const_cast<T*>(&r_item) > &(*begin)){
            if (res){
                std::swap(indexs[l_dis], indexs[r_dis]);//not working
            }
        }

        return res;
    });
    return indexs;

}

int main()
{
    std::vector<int> a{1,5,3,4,7};
    std::vector<int> indexes=sorta(a.begin(),a.end(),[](const int& a,const int& b){return a<b;});

    std::cout<<"\n";
    for(auto i:indexes)
    {
        std::cout<<i<<"\n";
    }
}

live

编写此类函数的其他解决方案也很受欢迎(没有制作和排序std :: pair)

3 个答案:

答案 0 :(得分:5)

std::sort使用O(n*log(n))复杂度的排序。例如,它可以是quicksort的优化版本。它选择一个枢轴,在枢轴之前移动较小的元素,在枢轴之后移动较大的元素,并在第一个和第二个半部分递归地重复它。

这也意味着std::sort允许(并且需要快速)在排序期间多次移动vector的元素。比较功能将在当前位置看到它们。

奇数向量外地址表明std::sort在排序期间也会将元素移动到堆栈中。具体来说:(g ++ 4.8.2)例如__unguarded_linear_insert中的bits/std_algo.h将其参数移动到堆栈。


在排序过程中元素的原始位置会丢失。如果要使用原始位置,则必须使用std::pair或其他方法存储原始位置。


来自Angew的回答:同样重要的是你的比较器不一致。这是未定义的行为,可以为segfaults提供不同的输入。

答案 1 :(得分:4)

首先,您有未定义的行为,因为您传递给std::sort的谓词不是内部一致的 - 它可能会报告a < bb < a在同一时间。

即使没有这个,std::sort的目的是排序范围 - 在其中交换元素,以便它们按照正确的顺序(根据谓词)。因此,元素当然会在范围内移动。

答案 2 :(得分:3)

它会打印出不同的地址,因为有些元素存储在堆栈中的临时变量中,而其他元素则被移动。

5**0x25e5014    // This is in the original array
3**0x25e5018    // This too
3**0x7fff93d5d3e0   // This is on the stack

“较小”的内存地址通常对应于文件图像中的值(函数地址,全局变量等),而较高的内存地址通常是堆或堆栈分配的地址。 0x7fff ********通常在64位程序的堆栈中,但这只是我个人的调试经验,而不是一成不变。