理解编译器在解决函数重载时的选择

Understanding compiler choice in resolving function overloading

我在 int= 32 位(gcc arm none eabi、cortex M3、GCC 版本 9)的平台上编译,方言设置为 C++17。

我有一个重载方法,其中包含同一方法的模板版本和普通版本。正常重载位于私有基 class 中,但已通过 using 子句公开。

class MemoryStream {
  public:
    inline void write(bool value);
    inline void write(uint8_t value);
    inline void write(uint16_t value);
    inline void write(uint32_t value);
    inline void write(int16_t value);
    inline void write(int32_t value);
}

class WriteStream :private MemeoryStream {
  public:
    using MemoryStream::write;

  template<typename T>
    typename std::enable_if<std::is_integral<T>::value>::type WriteStream::write(T obj){
       MemoryStream::write(obj);
    }
}

当我使用数字文字调用方法时,即

    txStream.write(0U);

我收到以下错误:

In instantiation of 'typename std::enable_if<std::is_integral<_Tp>::value>::type  WriteStream::write(T) [with T = unsigned int; typename std::enable_if<std::is_integral<_Tp>::value>::type = void]':
error: call of overloaded 'write(unsigned int&)' is ambiguous

1.) 为什么没有选择普通重载函数,因为它们是通过 using 子句导入的,如果我调用

txStream.write<uint32_t>(0U);

constexpr uint32_t Zero =0;
txStream.write<uint32_t>(Zero);

他们正确无误地解决了?

unsigned int 是否与 uint32_t 不同?

2.) 为什么编译器将数字文字转换为引用???如错误信息所示:'call of overloaded write(unsigned int&) is ambiguous'

unsigned int 是与固定宽度类型不同的类型(即使其中一个类型固定的类型将具有与 unsigned int 相同的大小),因此对于您的代码,编译器不会不知道应该调用 write 的哪个重载。

您需要为 unsigned intint 提供重载。

考虑一下如果在一个系统上使用 32 位整数调用 write(uint32_t) 而在另一个具有 16 位整数的系统上 完全相同的代码 会调用 write(uint16_t)(或用“64”代替“16”)。或者如果一个系统写入 32 位数字而另一个系统读取不同大小的数字,则会出现相应的混乱。

您收到不明确的重载错误的原因是,unsigned int 可以 隐式转换为 MemoryStream::write 的任何重载参数类型.由于 unsigned int 没有直接重载,编译器不知道 select:

哪个重载
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type WriteStream::write(T obj){
    //which overload for MemoryStream::write should be selected here?       
    MemoryStream::write(obj);
}

如果您显式声明模板参数 txtStream.write<uint32_t>(0U);,则不会发生这种情况,因为参数 0L 会隐式转换为 uint32_t。由于存在 uint32_tMemoryStream::write 的直接重载,因此您不会收到任何歧义错误。

至于你的第二个问题,在 WriteStream::write 的正文中,obj 是对 unsigned int 的左值引用,尽管从你的初始函数调用开始:txtStream.write(0L) 参数是一个数字文字:

template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type WriteStream::write(T obj){
   //obj is an lvalue reference within this scope to whatever T is
   //  deduced to be (unsigned int in your example)
   MemoryStream::write(obj);
}