"no user-provided default constructor" 仅对 gcc 发出警告

"no user-provided default constructor" warning only with gcc

以下代码在使用 gcc 编译时发出警告,但仅适用于版本 <= 9.3

#include <array>
#include <iostream>

template <std::size_t size>
struct A {
using atype = std::array<double, size>;

template <std::size_t index = 0>
static constexpr void fill_array(atype& res)
{
    std::get<index>(res) = 1;
    if constexpr (index < (size - 1))
           fill_array<index + 1>(res);
}
static constexpr atype get_array()
{
    atype res;
    fill_array(res);
    return res;
}
};


int main()
{
    auto x = A<3>::get_array();
    for (const auto e: x)
       std::cout << e << ' ';
}

godbolt 上进行测试。我正在使用 -Wall -pedantic -std=c++17 -O3 进行编译。发出的警告是

In file included from <source>:1:

<source>: In instantiation of 'static constexpr A<size>::atype A<size>::get_array() [with long unsigned int size = 3; A<size>::atype = std::array<double, 3>]':

<source>:26:30:   required from here

/opt/compiler-explorer/gcc-9.3.0/include/c++/9.3.0/array:94:12: note: 'using atype = struct std::array<double, 3>' {aka 'struct std::array<double, 3>'} has no user-provided default constructor

   94 |     struct array

      |            ^~~~~

/opt/compiler-explorer/gcc-9.3.0/include/c++/9.3.0/array:110:56: note: and the implicitly-defined constructor does not initialize 'double std::array<double, 3>::_M_elems [3]'

  110 |       typename _AT_Type::_Type                         _M_elems;

      |                                                        ^~~~~~~~

Compiler returned: 0

虽然 double 的 std::array 未初始化,但实际上递归模板例程会初始化 res 的所有元素。因此,警告不是“真实的”。原则上,我可以通过调用 fill_array<1> 来“破解”代码,从而跳过 0 组件的初始化。但是模板函数的代码只有在用给定的模板参数实例化时才会生成,所以同样,在上面的代码中,编译器永远不会跳过生成 fill_array<0>.

不幸的是,这个警告只出现在 gcc 9.3 版本之前。此外,clang 不会发出警告。 更奇怪的是,当函数没有嵌入到 class 中时,警告消失了。使用以下代码:

#include <array>
#include <iostream>

constexpr std::size_t size = 3;

using atype = std::array<double, size>;

template <std::size_t index = 0>
void fill_array(atype& res)
{
    std::get<index>(res) = 1;
    if constexpr (index < (size - 1))
           fill_array<index + 1>(res);
}

atype get_array()
{
    atype res;
    fill_array(res);
    return res;
}

int main()
{
    auto x = get_array();
    for (const auto e: x)
       std::cout << e << ' ';
}

尽管与第一个代码明显相同,但未显示任何警告。测试一下 here.

它看起来像是旧版本静态分析器中的一个错误,它无法检测到您为元素赋值并警告您使用未初始化的 class 和消息不能编译器标志避免 - 禁用学究模式并且所有警告都不会删除它,因为 res 应该是返回的常量,它必须被初始化。

抑制它的最简单方法是添加值初始化,这对 compile-time 初始化没有任何成本:

    atype res {};

另外,看起来更惯用的 std::generate 将适用于 C++20。 注意 - 对于 C++17,您的 fill_array() 不是 constexpr 由于返回 atype res 但您没有看到错误,因为您允许 x 为运行时。

适用于 C++17

get_array
static constexpr atype get_array()
{
    atype res {};
    for (std::size_t i = 0; i < size; i++)
        res[i] = 1;
    return res;
}