7

While thinking about solution for question std::initializer list from already existing std::array without enumerating each element, I developed similar mechanism as bolov did, but not constructing the object, but rather only initializer list. I was surprised that my solution did not work and I couldn't figure out why.

#include <initializer_list>
#include <iostream>
#include <array>

template<typename T, std::size_t N, std::size_t... Is>
std::initializer_list<T> array_to_init_list_helper(std::array<T, N> arr, std::index_sequence<Is...>)
{
    return {arr[Is]...};
}

template<typename T, std::size_t N>
std::initializer_list<T> array_to_init_list(std::array<T, N> arr)
{
    return array_to_init_list_helper(arr, std::make_index_sequence<N>{});
}

int main()
{
    std::array<int, 5> arr{1, 2, 3, 4, 5};
    auto init_list = array_to_init_list(arr);
    for (auto val : init_list)
        std::cout << val << " ";
}

I am getting random values, while I would expect to get values of arr.

1
  • 1
    std::initializer_list is not a container - it's basically just a pair of pointers. In effect, array_to_init_list_helper returns pointers to local variables. From the article: "Copying a std::initializer_list does not copy the underlying objects." Commented Aug 7, 2017 at 13:10

2 Answers 2

1

Going through the assembly here (clang 4.0.0) and/or here (GCC 7.1), it is clear that the std::initializer_list is using a dangling pointer, which (as we all know) produces seemingly random output.

EDIT

This result is of course in line with the comment made by Igor Tandetnik, quoting cppreference.com:

The underlying array is a temporary array of type const T[N], in which each element is copy-initialized (except that narrowing conversions are invalid) from the corresponding element of the original initializer list. The lifetime of the underlying array is the same as any other temporary object, except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary (with the same exceptions, such as for initializing a non-static class member). The underlying array may be allocated in read-only memory.

Sign up to request clarification or add additional context in comments.

2 Comments

Would returning by const reference to initilizer list change anything?
@Zereges I'm not sure what you mean, but if you mean to change the return value to const std::initializer_list<T> & then that does not work either. In fact this will produce a returning reference to temporary warning and quite possibly a segmentation fault.
1

[dcl.init.list]/5 An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated an array of N elements of type E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E> object is constructed to refer to that array.

[dcl.init.list]/6 The lifetime of the array is the same as that of the initializer_list object.

This last part is important for your example. array_to_init_list_helper returns a copy of the original initializer_list - but the underlying array's lifetime ends with that of the original. The copy refers to the object whose lifetime has already ended. Your program then exhibits undefined behavior.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.