Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Undefined behaviour in vector of vectors cast

Tags:

c++

Why does this code write an undefined number of seemingly uninitialized integers?

#include <iostream>
#include <vector>
using namespace std;


int main()
{
    for (int i : vector<vector<int>>{{77, 777, 7777}}[0])
        cout << i << ' ';
}

I expected the output to be 77 777 7777.

Is this code supposed to be undefined?

like image 933
GT 77 Avatar asked Apr 07 '20 15:04

GT 77


3 Answers

vector<vector<int>>{{77, 777, 7777}} is a temporary and then using vector<vector<int>>{{77, 777, 7777}}[0] in ranged-for will be an undefined behaviour.

You should create a variable first, like

#include <iostream>
#include <vector>
using namespace std;


int main()
{
    auto v = vector<vector<int>>{{77, 777, 7777}};
    for(int i: v[0])
        cout << i << ' ';
}

Also If you use Clang 10.0.0 it gives warning about this behaviour.

warning: object backing the pointer will be destroyed at the end of the full-expression [-Wdangling-gsl] vector>{{77, 777, 7777}}[0]

like image 166
Gaurav Dhiman Avatar answered Nov 20 '22 02:11

Gaurav Dhiman


This is because the the vector you are iterating over will be destroyed before entering the loop.

This is what typically happens:

auto&& range = vector<vector<int>>{{77, 777, 7777}}[0];
auto&& first = std::begin(range);
auto&& last = std::end(range);
for(; first != last; ++first)
{
    int i = *first;
    // the rest of the loop
}

The problems start at the first line because it's evaluated as follows:

  1. Firstly construct the vector of vectors with the given arguments and that vector becomes a temporary because it has no names.

  2. Then the range reference is bound to the subscripted vector which will only be valid as long as the vector that contains it is valid.

  3. Once the semicolon is reached the temporary vector is destroyed and in its destructor it will destroy and deallocate any stored vectors which includes the subscripted one.

  4. You end up with a reference to a destroyed vector which will be iterated over.

To avoid this problem there are two solutions:

  1. Declare the vector before the loop so it lasts until its scope ends which includes the loop.

  2. C++20 comes with an init statement which is provided to solve these problems and is better than the first approach if you want the vector to be destroyed after the loop immediately:

    for (vector<vector<int>> vec{{77, 777, 7777}}; int i : vec[0])
    {
    }
    
like image 45
dev65 Avatar answered Nov 20 '22 00:11

dev65


vector<vector<int>>{{77, 777, 7777}}[0]

I expect that this is dangling.

Though the definition of a ranged-for assures that the RHS of the colon remains "alive" for the duration, you're still subscripting a temporary. Only the result of the subscript is kept, but that result is a reference, and the actual vector cannot survive past the full-expression in which it's declared. That does not describe the whole loop.

like image 6
Asteroids With Wings Avatar answered Nov 20 '22 02:11

Asteroids With Wings