First of all this is curiosity kind of question, I would never write code like this in real life.
Following code behaves differently with -O3 -std=c++14 and -O3 -std=c++17 flags, in C++14 I get bad alloc, I presume from copy construction from a garbage std::string:
#include<algorithm>
#include<numeric>
#include<vector>
#include<string>
#include<iostream>
using namespace std;
static auto results = std::initializer_list<string>{"1 ",
"2"};
string f() {
auto result = std::accumulate(results.begin(), results.end(), string(""));
return result;
}
int main()
{
return f().size();
}
My guess is that C++17 version keeps the underlying array alive longer than C++14 version, but I found no relevant changes to initializer list from C++14 to C++17 on cppreference so I am confused. Is this just UB being UB, or did language change?
P.S. I know how to fix this, using static const auto& results
works, like mentioned before this is just a question about corner cases of the language.
This has to do with guaranteed copy elision, a new language feature in C++17.
This line (reduced):
static auto results = std::initializer_list<string>{x, y};
In C++14 constructs an initializer list and then moves it into results
- which immediately dangles because initializer_list
doesn't manage any lifetimes (a std::initializer_list
has a backing const array with the same lifetime as the initial object - once the initial initializer_list
is destroyed at the end of the line, so is the backing array).
In other words, in C++14, this program has undefined behavior.
In C++17 it behaves exactly like:
static std::initalizer_list<string> results{x, y};
In this case, the backing array has the same lifetime as results
, which is for the length of the program. This program has well-defined behavior.