Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ insert empty vector can not use braces

Tags:

c++

std

vector

Codes below doesnt' works properly, when I reference to it, it causes Member call on null pointer error, if it's not supported, why it allow the use of insert(end, {})(No compile error).

#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<vector<pair<int, int>>> vv;
    auto it = vv.insert(vv.end(), {});
    cout << it->size() << endl;
}

When I changed {} to vector<pair<int, int>>, no error shows and print 0.

Solved by comments, It calls iterator insert( const_iterator pos, std::initializer_list<T> ilist ), So change it to insert(end, {{}}) could work.

like image 596
BAKE ZQ Avatar asked Apr 10 '26 08:04

BAKE ZQ


2 Answers

According to cppreference – std::vector<T,Allocator>::insert(), all flavors of insert() return the iterator of first inserted element or the insertion position if nothing was inserted.

In OPs exposed MCVE:

#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<vector<pair<int, int>>> vv;
    auto it = vv.insert(vv.end(), {});
    cout << it->size() << endl;
}

the line

auto it = vv.insert(vv.end(), {});

doesn't use the
iterator insert( const_iterator pos, const T& value );
as it might be expected but the
iterator insert( const_iterator pos, std::initializer_list<T> ilist );.

Hence, there are 0 elements inserted and the insert() returns the end() iterator which was passed.

So,

cout << it->size() << endl;

accesses the contents of end() which is Undefined Behavior.

I made a small sample to demonstrate this:

#include <initializer_list>
#include <iostream>
#include <vector>

template <typename T>
struct Container: std::vector<T> {
  using typename std::vector<T>::iterator;
  using typename std::vector<T>::const_iterator;
    
  iterator insert( const_iterator pos, const T& value )
  {
    std::cout << "insert single value\n";
    return std::vector<T>::insert(pos, value);
  }
  
  iterator insert( const_iterator pos, std::initializer_list<T> ilist )
  {
    std::cout << "insert initializer list\n";
    return std::vector<T>::insert(pos, ilist);
  }
};

using namespace std;
int main() {
    Container<vector<pair<int, int>>> vv;
    auto it = vv.insert(vv.end(), {});
    cout << /*it->*/vv.size() << endl;
}

Output:

insert initializer list
0

Live Demo on coliru

Note:

I'm aware that it's, at least, bad style to overload std::vector. I did this exceptionally to show the used function call (and I didn't have a better idea how to achieve this).

like image 193
Scheff's Cat Avatar answered Apr 11 '26 21:04

Scheff's Cat


The line auto it = vv.insert(vv.end(), {}); is using the form iterator insert( const_iterator pos, std::initializer_list<T> ilist );.

As int the line auto it = vv.insert(vv.end(), {});, the initlizer list have no element so no element is inserted in the vector vv.

As there is no element is inserted so it is same as vv.end() for which it is invalid to deference.

You can verify it via checking the length of vv after insertion. See sample code here to check it.

like image 24
cse Avatar answered Apr 11 '26 23:04

cse