在for循环中对std::array进行下标访问时的符号类型转换警告

编程语言 2026-07-09

据我理解,由于使用 for 循环来定义运行时控制流,element_declaration (e 在下面的示例中) 不应被视为常量表达式(尽管它复制了数组 xconstexpr 个元素)。为了在不产生符号转换警告的情况下对数组进行索引,我们必须使用 size_t 或者一个常量表达式。因此,为什么下面的程序在没有警告的情况下编译(对它来说 e 必须是一个常量表达式)?

#include <array>

struct Foo 
{
    enum X
    {
        a,
        b,
        c,
    };

    static constexpr std::array x { a, b, c };
};

int main()
{
    std::array<int, 3> array {};

    for (auto e: Foo::x)
        array[e] = 1; 

    return 0;
}

然而,将数组赋值行改为(或在 [] 运算符内部进行任何算术运算)突然会触发警告:

array[e * 1] = 1;
warning: conversion to ‘s
td::array<int, 3>::size_type’ {aka ‘long unsigned int’} from ‘int’ may change the si
gn of the result [-Wsign-conversion]
   20 |         array[e * 1] = 1;

Demo

带底层类型显示的警告演示

使用gcc 13.3.0(禁用扩展和优化)。

注:gcc和 clang都在使用 unsigned int 作为底层类型,但只有gcc会发出警告。msvc使用 int,但没有警告。

解决方案

对于没有固定底层类型的未作用域枚举,编译器将使用哪种底层类型是实现定义的。引自C++23标准(9.7.1枚举声明)

7对于底层类型未固定的枚举,底层类型是可以表示该枚举中定义的所有枚举值的整型类型。如果没有整型类型能表示所有的枚举值,枚举就是不合法的。实现定义使用哪种整型作为底层类型,除了底层类型不得大于int,除非某个枚举值不能适配int或 unsigned int。 如果枚举值列表为空,则底层类型就像枚举只有一个值为0 的枚举。

看起来在第一个程序中,编译器将枚举的底层类型设为无符号整数类型。

下面是一个演示程序(x86-64 gcc 16.1)。

#include <iostream>
#include <array>
#include <type_traits>

struct Foo 
{
    enum X
    {
    a,
    b,
    c,
    };

    static constexpr std::array x { a, b, c };
};

int main()
{
    std::cout << std::boolalpha
        << std::is_same_v<std::underlying_type_t<Foo::X>, unsigned int>
        << '\n';
}

其输出为

true

因此,将无符号整数类型 unsigned int 的值转换为类型 size_t 没有问题。

在第二个代码片段中,使用有符号类型 int 在将负值转换为无符号整数类型 size_t 时可能会造成问题。因此,编译器可能会发出警告。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章