仅针对gcc

问题描述

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

尽管明显与第一个代码相同,也没有显示警告。测试here

  • 这两个代码间的编译器行为是否不同?

  • 是否可以仅在不引入开销的情况下对此变量取消警告?

解决方法

它看起来像是旧版静态分析器中的错误,无法检测到您为元素分配值的事实,并警告您使用未初始化的类,并且编译器标志无法避免消息-禁用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;
}