I'm trying to overload some functions to work with several data types. This is trivial when I'm passing variables as parameters, thusly:
void fnc(int32_t d){
Serial.println((String) "int32 = " + d);
}
void fnc(char* d){
Serial.println((String) "string = " + d);
}
void fnc(bool d){
if(d){ Serial.println((String) "bool = " + "true"); }
else{ Serial.println((String) "bool = " + "false"); }
}
void fnc(float d){
Serial.println((String) "float = " + d);
}
int32_t anInt = 29339222;
char* aStr = "baloney";
bool aBool = false;
float aFloat = 3.1415;
void setup() {
Serial.begin(9600);
fnc(anInt);
fnc(aStr);
fnc(aBool);
fnc(aFloat);
}
(Note, this is on Arduino, hence setup() instead of main().)
Results in, as you would expect:
int32 = 29339222
string = baloney
bool = false
float = 3.14
Now then, I need to pass literal values as well as variables, like so:
fnc(8728787);
fnc("spamoney");
fnc(true);
fnc(2.718);
The compiler complains about the last line calling fnc(2.718):
sketch_apr1a.ino:34:12: error: call of overloaded 'fnc(double)' is ambiguous
fnc(2.718);
I imagine that this is because as a literal, the parameter provides the compiler with no type information. The actual bits of the value could be interpreted as any 4 byte type (float is 4-bytes on Arduino) and int32_t is a 4 byte type.
So, a couple of questions:
Note: I don't want to use a template function because I need to handle the different types with different algorithms.
Note note: I have solved this for now by simply creating multiple functions i.e. fncInt, fncStr, fncBool, etc. This works but...it rubs my OCD the wrong way.
Note note note: This will be part of a library. I'd also like to avoid forcing whoever uses it to do fnc((float) 2.718) for the same reasons as the previous note. Maybe it's silly. I dunno.
Update: help from Miles Budnik and 273K much appreciated. It does indeed compile if I change func(float d) to func(double d).
However, if I try passing an int it fails again.
int anInt2 = 12;
int32_t anInt = 29339222;
float aFloat = 3.1415;
void setup() {
Serial.begin(9600);
fnc(anInt);
fnc(aFloat);
fnc(8728787);
fnc(2.718);
fnc(anInt2);
}
sketch_apr1a.ino:28:13: error: call of overloaded 'fnc(int&)' is ambiguous fnc(anInt2);
Also, if I change from int32 to int64, it also fails, again on the double literal.
void fnc(int64_t d){
//Serial.println((String) "int32 = " + d);
}
int64_t anInt = 29339222;
float aFloat = 3.1415;
void setup() {
Serial.begin(9600);
fnc(anInt);
fnc(aFloat);
fnc(8728787);
fnc(2.718);
fnc(anInt2);
}
sketch_apr1a.ino:27:14: error: call of overloaded 'fnc(long int)' is ambiguous fnc(8728787); ^
I appreciate the advice, I have indeed tried understanding this from the books; I'm afraid I'm just quite confused by the whole thing. Thank you for your help.
Your issue is that the literal 2.718
has the type double
, not float
, and there is no fnc(double)
overload. C++ allows up to one built-in conversion and one user-defined conversion to be implicitly added when performing overload resolution. There are built-in conversions from double
to int32_t
, bool
, and float
and none of them are considered "better" than any of the others by the compiler since they are all lossy conversions. Thus the call is ambiguous because it doesn't know if you want to call fnc(int32_t)
, fnc(bool)
or fnc(float)
.
If you made your floating-point overload accept a double
instead of a float
there would be an exact match, which is considered "better" than a match requiring a conversion, so that would be chosen.
Also note that float
to double
conversion is also considered "better" than the others mentioned since it's lossless, while the others are lossy, so for a call to fnc(aFloat)
the compiler will also choose fnc(double)
over fnc(int32_t)
or fnc(bool)
.
Alternatively, you can append the f
suffix to your floating-point literal to explicitly make it a float
. For example fnc(2.718f)
is not ambiguous since it's an exact match for fnc(float)
.
- Why doesn't the compiler have a similar problem with 8728787 as a parameter? That should be just as ambiguous, no?
This is a bit complicated. On Arduino, int
can be either 16-bit or 32-bit, depending on the exact board.
8728787
will be either int
(if int
is 32-bit) or long int
(if int
is 16-bit) since 8728787
is too big to be represented in 16 bits.int32_t
will be an alias for either int
(if int
is 32-bit) or long int
(if int
is 16-bit).In either case, the type of 8728787
will exactly match int32_t
. Since it's an exact match, fnc(int32_t)
will be chosen over the other options.
If you tried to pass a 64-bit integer to fnc
you would get the same ambiguity as the double
to float
case. For example, the following is ambiguous for the same reason as the double
call:
int64_t aLongInt = 123;
fnc(aLongInt);