Initialization of std :: array with static storage duration with parameter package extension and optional value

When asking for another question, I recently stumbled upon some strange GCC behavior when initializing std::array with an extension of the options package, followed by another element. I already discussed this briefly with Jarod42 in the comments there , but I find it better to ask, as a new question.

For example, consider the following code, which should provide a utility function make_array , which takes an arbitrary number of parameters and std::forward them to initialize std::array . The leading tag parameter determines whether the array should be completed by default T (selected via std::true_type ) or not (selected through std::false_type ). Then I create an integer array of any kind once with static and once with automatic storage time. Elements of the array are finally printed.

 #include <array> // std::array #include <cstddef> // std::size_t #include <iomanip> // std::setw #include <ios> // std::left, std::right #include <iostream> // std::cout #include <string> // std::string #include <type_traits> // std::false_type, std::true_type // This is only used for visualization. template <typename T, std::size_t N> void print_array(const std::string& name, const std::array<T, N>& arr) { std::cout << std::setw(20) << std::left << (name + ":") << std::right << "{"; for (auto iter = arr.cbegin(); iter != arr.cend(); ++iter) std::cout << (iter != arr.cbegin() ? ", " : "") << std::setw(2) << *iter; std::cout << "}\n"; } // Create a `std::array<T>` with elements constructed from the provided // arguments. template <typename T, typename... ArgTs> static constexpr auto make_array(std::false_type, ArgTs&&... args) noexcept { std::array<T, sizeof...(args)> values = { { std::forward<ArgTs>(args)... } }; return values; } // Create a `std::array<T>` with elements constructed from the provided // arguments followed by a default-constructed `T`. template <typename T, typename... ArgTs> static constexpr auto make_array(std::true_type, ArgTs&&... args) noexcept { std::array<T, sizeof...(args) + 1> values = { { std::forward<ArgTs>(args)..., T {} } }; return values; } namespace /* anonymous */ { const auto values_no_static = make_array<int>(std::false_type(), 1, 2, 3, 4); const auto values_yes_static = make_array<int>(std::true_type(), 1, 2, 3, 4); } int main() { const auto values_no_automatic = make_array<int>(std::false_type(), 1, 2, 3, 4); const auto values_yes_automatic = make_array<int>(std::true_type(), 1, 2, 3, 4); print_array("static yes", values_yes_static); print_array("static no", values_no_static); print_array("automatic yes", values_yes_automatic); print_array("automatic no", values_no_automatic); } 

I expect the arrays {1, 2, 3, 4} and {1, 2, 3, 4, 0} printed twice. This is what Klang does. GCC, however, prints {0, 0, 0, 0, 0} for a completed array with a static storage duration. If I replace trailing T {} in the list of initializers with -1 , I get {0, 0, 0, 0, -1} . Thus, it seems that the addition of finite elements leads to the fact that the package of parameters extends to all zeros. But only if the resulting std::array has a static storage duration.

Did I call undefined behavior or is it a bug in GCC? If this behavior is undefined, I would be grateful for the official reference to the standard. I already know a simple way around this problem (see my answer to the question above), and I'm not interested in avoiding the problem. Rather, I want to know if the code is well-formed and, if so, which compiler is right.

Full output using GCC:

 $ g++ --version g++ (GCC) 5.1.0 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ $ g++ -std=c++14 -o main_gcc -Wall -Wextra -Werror -pedantic main.cxx $ $ ./main_gcc static yes: { 0, 0, 0, 0, 0} static no: { 1, 2, 3, 4} automatic yes: { 1, 2, 3, 4, 0} automatic no: { 1, 2, 3, 4} 

Using Clang:

 $ clang --version clang version 3.6.2 (tags/RELEASE_362/final) Target: x86_64-unknown-linux-gnu Thread model: posix $ $ clang -std=c++14 -o main_clang -Wall -Wextra -Werror -pedantic main.cxx -lstdc++ $ $ ./main_clang static yes: { 1, 2, 3, 4, 0} static no: { 1, 2, 3, 4} automatic yes: { 1, 2, 3, 4, 0} automatic no: { 1, 2, 3, 4} 
+4
source share
2 answers

Minimum playback example:

 #include <array> constexpr auto make_array(int i) { std::array<int, 2> values = { i, 0 }; return values; } constexpr auto arr = make_array(1); static_assert(arr[0] == 1, ""); 

Demo with HEAD . To date, we can safely say that UB is not induced.

Notice how we can get around the error:

+3
source

I reported this to the GCC developers. It was recognized as bug 67104 and was fixed in August 2015 for GCC 6 and back ported to GCC 5.3. You can use the link to the online compiler provided in Columbus's answer to check.

+1
source

All Articles