问题描述
以下代码在使用gcc进行编译时会发出警告,但仅限版本
#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不会发出警告。 更奇怪的是,当函数未嵌入类时,警告消失。使用以下代码:
#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 << ' ';
}
解决方法
它看起来像是旧版静态分析器中的错误,无法检测到您为元素分配值的事实,并警告您使用未初始化的类,并且编译器标志无法避免消息-禁用pedantic模式,并且所有警告都不能将其删除,因为res应该是返回的常量,所以必须对其进行初始化。
抑制它的最简单方法是添加值初始化,这对于编译时初始化没有任何花费:
atype res {};
另外,看起来更惯用的std::generate
也可以在C ++ 20中使用。 注意-在C ++ 17中,由于返回了constexpr
,所以fill_array()不是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;
}