"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;
}
以下代码在使用 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;
}