I am compiling on a platform where int= 32 bits (gcc arm none eabi, cortex M3, GCC version 9) with the dialect set to C++17.
I have a overloaded a method with both template versions and plain versions of the same method. The normal overloads are in a private base class but have been exposed through a using clause.
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);
}
and
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);
}
}
when I call the method using a numeric literal i.e.
txStream.write(0U);
I get the following error:
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.) Why are the plain overloaded functions not being selected as they are being imported through the using clause and if I call
txStream.write<uint32_t>(0U);
or
constexpr uint32_t Zero =0;
txStream.write<uint32_t>(Zero);
They resolve without error ?
Is unsigned int seen a a distinct type from uint32_t ?
2.) Why is the compiler converting the numeric literal to a reference ??? as shown by the error message: 'call of overloaded write(unsigned int&) is ambiguous'
The reason you are getting an ambiguous overload error, is because an unsigned int
could be implicitly converted to any of the overload argument types for MemoryStream::write
. Since there is no direct overload for unsigned int
, the compiler does not know which overload to 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);
}
This does not happen if you explicitly state the template parameter txtStream.write<uint32_t>(0U);
, because the argument 0L
is implicitly converted to uint32_t
. Since a direct overload for MemoryStream::write
for uint32_t
exists, you do not get any ambiguity errors.
As for your second question, within the body of WriteStream::write
, obj
is an lvalue reference to an unsigned int
, even though, from your initial function call: txtStream.write(0L)
the argument is a numerical literal:
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);
}