c++ insert empty vector can not use braces

Viewed 286

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.

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).

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.

Related