C++ 入门枚举和隐式转换

问题描述

你好,我有这个来自 C++ 入门第 5 版:

// unscoped enumeration; the underlying type is machine dependent

enum Tokens {INLINE = 128,VIRTUAL = 129};

void newf(unsigned char);

void newf(int);

unsigned char uc = VIRTUAL;

newf(VIRTUAL); // calls newf(int)

newf(uc); // calls newf(unsigned char)

enum 令牌只有两个枚举器,其中较大的一个值为 129。该值可以由类型 unsigned char 表示,许多编译器将使用 unsigned char 作为令牌的基础类型。无论其底层类型如何,Tokens 的对象和枚举数都被提升为 intenum 类型的枚举数和值不会提升为 unsigned char,即使枚举数的值适合。

  • 但是如果我明确指定与函数参数的整数值完全匹配的基础类型,那么我认为不会发生隐式转换:

    enum days : unsigned char{sat = 1,sun,mon,tue,wed,thu,fri};
    
    void foo(unsigned char x){
        std::cout << "foo(unsigned char)\n";
    }
    
    void foo(int){
       std::cout << "foo(int)\n";
    }
    
    foo(tue); // foo(unsigend char)
    

如您所见,枚举值 tue 没有提升为 int,我得到的是 foo(unsigned char) 而不是 foo(int) 的版本。

  • 那么他的意思是如果枚举基础类型与函数参数不同,那么枚举器不会提升为 char 而是提升为 int 或大整数类型?

  • 我认为在他的 enum tokens 示例中,如果基础类型是 unsigned char,那么函数调用中将没有整体提升:newf(VIRTUAL); // calls newf(int) 和 {{ 的版本1}} 被选中。你怎么看?

解决方法

来自 cppreference 对 enums 的描述:

无作用域枚举类型的值可隐式转换为整数类型。如果基础类型不固定,则该值可转换为以下列表中第一个能够保存其整个值范围的类型:intunsigned intlong、{{1} }、unsigned longlong long,具有更高转换等级的扩展整数类型(按等级顺序,有符号优先于无符号)(C++11 起)。如果底层类型是固定的,则可以将值转换为其底层类型(在重载决议中首选),然后可以提升。

所以您关于将选择 unsigned long long 的哪个重载是正确的,但是您错误的是没有发生隐式转换。 foo 仍然是与 days 不同的类型,但它会隐式转换为一种。

您也错了,如果实现恰好选择 unsigned char 作为 unsigned char 的基础类型,它因此将隐式转换为 enum Tokens。如果您自己修复底层类型而不是让实现来决定,则会隐式转换为比 unsigned char only 更窄的底层类型。

您引用的 Primer 中的文本仅描述了您未明确指定基础类型时的情况。

引自 draft standard,expr.conv.prom:

基础类型不固定的无作用域枚举类型的纯右值可以转换为以下第一个类型的纯右值,该类型可以表示枚举的所有值([dcl.enum]):{{1} }、intintunsigned intlong intunsigned long int。如果该列表中的任何类型都不能表示枚举的所有值,则无作用域枚举类型的纯右值可以转换为扩展整数类型的纯右值,其最低整数转换等级 ([conv.rank]) 大于long long int 的等级,其中可以表示枚举的所有值。如果有两种这样的扩展类型,则选择有符号的一种。

基础类型是固定的 ([dcl.enum]) 的无作用域枚举类型的纯右值可以转换为其基础类型的纯右值。此外,如果可以对其底层类型应用整型提升,那么底层类型固定的无作用域枚举类型的纯右值也可以转换为提升的底层类型的纯右值。

来自over.ics.rank

如果两者不同,则将基础类型固定为其基础类型的枚举的转换提升到提升的基础类型的转换要好。