Same clang, different results for std::initializer_list program with -std=c++14/-std=c++17

145 Views Asked by At

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();
}

https://godbolt.org/z/H-Xzei

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.

1

There are 1 best solutions below

5
On BEST ANSWER

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.