使用枚举基编写枚举时出现不明确的重载,但仅使用 clang

Ambiguous overload when writing an enum with an enum-base, but only with clang

我想使用 operator<< 来编写具有指定基类型的枚举。令我惊讶的是,似乎我必须自己写出运算符。比如我想写的代码是

#include <iostream>
enum myenum : uint16_t
{
    X = 0,
};
int main ()
{
    std::cout << "Value is" << X << std::endl;
    return 0;
}

gcc 4.8 和 visual studio 2015 没有这个问题。 clang++-3.6 错误

# clang++-3.6 -std=c++11 -O0 ostream.cpp -o test.exe
ostream.cpp:18:29: error: use of overloaded operator '<<' is ambiguous (with operand types
      'basic_ostream<char, std::char_traits<char> >' and 'myenum')
    std::cout << "Value is" << X << std::endl;
    ~~~~~~~~~~~~~~~~~~~~~~~ ^  ~
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/ostream:181:7: note: 
      candidate function
      operator<<(unsigned short __n)
      ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/ostream:189:7: note: 
      candidate function
      operator<<(int __n);
      ^
      ... another 14 candidates along the same lines ...

总的来说,在严格遵守方面,我倾向于相信 clang,所以也许这个用法真的是模棱两可的。枚举当然可以转换为其他类型的整数。我希望编译器更喜欢与枚举的基类型对应的版本。

我可以通过使用其他一些语言功能而不是枚举来解决问题...:uint16_t 或者通过显式键入运算符,在这种情况下,clang 也是内容。

static inline std::ostream & operator<<(std::ostream & out, myenum const & s)
{
    out << static_cast<std::underlying_type<myenum>::type> (s);
    return out;
}

虽然这看起来很荒谬。 clang 的行为是预期的还是错误的?

叮当声问题。最小化重现:

enum myenum : unsigned short
{
    X = 0,
};

void f(unsigned short);
void f(int);
int main ()
{
    f(X);
    return 0;
}

[conv.prom]/4:

A prvalue of an unscoped enumeration type whose underlying type is fixed (7.2) can be converted to a prvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, a prvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to a prvalue of the promoted underlying type.

[over.ics.rank]/4.2 - 此项目符号由 CWG1601 作为针对 C++11 的 DR 添加:

Standard conversion sequences are ordered by their ranks: an Exact Match is a better conversion than a Promotion, which is a better conversion than a Conversion. Two conversion sequences with the same rank are indistinguishable unless one of the following rules applies:

  • [...]
  • A conversion that promotes an enumeration whose underlying type is fixed to its underlying type is better than one that promotes to the promoted underlying type, if the two are different.

myenum->unsigned shortmyenum->int 具有相同的等级(升级)。根据 [over.ics.rank]/4.2,第一个比第二个好。这里没有歧义。

OTOH,GCC 默默地调用了错误的重载(int 那个),这并没有那么好。