A confusing phenomenon about vector in C++14 and C++17 (looking for explanation!)

Revision en1, by Andycode3759, 2024-02-02 17:35:51

So here is the thing: recently I was doing some sustainable data structure problems while I found that the behavior of vector seems to vary from C++ standard versions. I did a little experiment:

#include <vector>
#include <cstdio>
using namespace std;
const int MAXN = 1000006;

int n;
int arr[MAXN];

struct SegTreeNode
{
    SegTreeNode(int _l = 0, int _r = 0, int _v = 0)
    {
        val = _v;
        ls = rs = -1;
        sl = _l, sr = _r;
    }
    int val;
    int ls, rs;
    int sl, sr;
};

vector<SegTreeNode> sgt;
int root;

inline int newNode(int sl, int sr, int val)
{
    sgt.push_back(SegTreeNode(sl, sr, val));
    return sgt.size() - 1;
}
int build(int l, int r)
{
    int t = newNode(l, r, 0);
    if (l == r)
    {
        sgt[t].val = arr[l];
        return t;
    }
    int mid = (l + r) >> 1;
    sgt[t].ls = build(l, mid);
    sgt[t].rs = build(mid + 1, r);
    sgt[t].val = sgt[sgt[t].ls].val + sgt[sgt[t].rs].val;
    return t;
}
int query(int idx, int l, int r)
{
    if (l <= sgt[idx].sl && sgt[idx].sr <= r)
        return sgt[idx].val;
    int mid = (sgt[idx].sl + sgt[idx].sr) >> 1;
    int res = 0;
    if (l <= mid)
        res += query(sgt[idx].ls, l, r);
    if (r > mid)
        res += query(sgt[idx].rs, l, r);
    return res;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", arr + i);
    root = build(1, n);
    int l, r;
    scanf("%d %d", &l, &r);
    printf("%d\n", query(root, l, r));
    for (int i = 1; i <= n; i++)
        printf("%d ", query(root, i, i));
    printf("\n");
    for (SegTreeNode nd : sgt)
        printf("val=%d [%d,%d] ls=%d rs=%d\n", nd.val, nd.sl, nd.sr, nd.ls, nd.rs);
    return 0;
}

It's just a basic segment tree with dynamically allocated nodes. I compiled the same code in both C++14 and C++17, and I got different output from two programs:

Input:
5
1 2 3 4 5
1 4

C++14:
8
4 4 4 4 4
val=8 [1,5] ls=-1 rs=-1
val=4 [1,3] ls=-1 rs=5
val=2 [1,2] ls=3 rs=-1
val=1 [1,1] ls=-1 rs=-1
val=2 [2,2] ls=-1 rs=-1
val=3 [3,3] ls=-1 rs=-1
val=8 [4,5] ls=7 rs=-1
val=4 [4,4] ls=-1 rs=-1
val=5 [5,5] ls=-1 rs=-1

C++17:
10
1 2 3 4 5
val=15 [1,5] ls=1 rs=6
val=6 [1,3] ls=2 rs=5
val=3 [1,2] ls=3 rs=4
val=1 [1,1] ls=-1 rs=-1
val=2 [2,2] ls=-1 rs=-1
val=3 [3,3] ls=-1 rs=-1
val=9 [4,5] ls=7 rs=8
val=4 [4,4] ls=-1 rs=-1
val=5 [5,5] ls=-1 rs=-1

Only the result of C++17 is correct. I also modified the program to use static arrays and the correct output appears in both C++14 and C++17. So I'm confident that the vector in C++14 is to blame.

Can anyone explain why it happens? I tried to compile the source into assembly code and I did found that there are differences, but I can't understand them: dynamic-segtree-cpp14.asm, dynamic-segtree-cpp17.asm. Do they provide clearer evidence?

Tags c++

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en1 English Andycode3759 2024-02-02 17:35:51 3073 Initial revision (published)