c++ - 使用带有参数包扩展和附加值的静态存储持续时间初始化 std::array

标签 c++ g++ language-lawyer variadic-templates c++14

在询问时another question最近,在用参数包扩展后跟另一个元素 初始化std::array 时,我偶然发现了 GCC 的一些奇怪行为。我已经与 Jarod42 简要讨论过这个问题 in the comments there但我认为最好将其作为一个新问题提出。

例如,考虑下面的代码,它应该提供一个实用程序 make_array 函数,该函数接受任意数量的参数并将它们std::forward发送到 std::array 初始化。前导标记参数选择数组是否应以默认构造的 T(通过 std::true_type 选择)或不(通过 std::选择)终止false_type).然后我创建一个整数数组,一次使用静态,一次使用自动存储持续时间。最终打印出数组元素。

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

我希望看到数组 {1, 2, 3, 4}{1, 2, 3, 4, 0} 打印两次。这就是 Clang 所做的。但是,GCC 为具有静态存储持续时间的终止数组打印 {0, 0, 0, 0, 0}。如果我用 -1 替换初始化列表中尾随的 T {},我得到 {0, 0, 0, 0, -1} 相反。所以似乎添加尾随元素会导致参数包扩展为全零。但前提是生成的 std::array 具有静态存储持续时间。

我是否调用了未定义的行为或者这是 GCC 中的错误?如果它是未定义的行为,我将感谢对标准的官方引用。 我已经知道解决此问题的简单方法(请参阅上述问题的 my answer)并且我不感兴趣避免问题。相反,我想知道代码是否格式正确,如果是,哪个编译器是正确的。

使用 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}

使用 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}

最佳答案

最小复制样本:

#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 .到目前为止,我们可以有把握地说没有诱发 UB。

请注意我们如何解决该错误:

关于c++ - 使用带有参数包扩展和附加值的静态存储持续时间初始化 std::array,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31762429/

相关文章:

c++ - 如何在 C++ 中进行 32 位十进制 float 乘法?

c++ - 类 TESTDLL_LIBSHARED_EXPORT TestDLL_lib

c++ - Visual Studio 2010 : extensions/discrepancies

c++ - libstdc++ - 不使用 qmake 静态链接

c++ - fatal error : string: No such file or directory compilation terminated

C++ - 枚举范围? Bjarne Stroustrup 书

android - 尝试使用 -pg [Termux] 时出现 "Undefined reference to ` mcount '"

c++ - 匿名临时对象和类模板参数推导 - gcc vs clang

c++ - constexpr构造函数不满足要求,但还是constexpr。为什么?

c++ - 可以使用 realloc 安全地重新分配普通可复制对象的存储吗?