36

What does this error message mean?

error: call of overloaded ‘setval(int)’ is ambiguous
huge.cpp:18: note: candidates are: void huge::setval(unsigned int)
huge.cpp:28: note:                 void huge::setval(const char*)

My code looks like this:

#include <iostream>
#define BYTES 8
using namespace std ;

class huge {
private:
    unsigned char data[BYTES];
public:
    void setval(unsigned int);
    void setval(const char *);  
};

void huge::setval(unsigned int t) {
    for(int i = 0; i< BYTES ; i++) {
        data[i] = t;
        t = t >> 1;
    }
}

void huge::setval(const char *s) {
    for(int i = 0; i< BYTES ; i++)
        data[i] = s[i];
}

int main() {
    huge p;
    p.setval(0);
    return 0;
}
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Barshan Das
  • 3,677
  • 4
  • 32
  • 46
  • 5
    I guess the answer to this question would depend on which version of the function you ARE trying to call. I could assume, but clearly I can't be sure either way unless you tell me. Would that work with the compiler I wonder... – Edward Strange Jan 12 '11 at 17:59
  • @KonradRudolph The literal `0` is a valid value for both `unsigned int` and `const char *`. – kitti Mar 23 '17 at 22:47
  • @RyanP Yeah, I'm generally aware. No idea what I meant back then. – Konrad Rudolph Mar 23 '17 at 22:51

6 Answers6

31

The literal 0 has two meanings in C++.
On the one hand, it is an integer with the value 0.
On the other hand, it is a null-pointer constant.

As your setval function can accept either an int or a char*, the compiler can not decide which overload you meant.

The easiest solution is to just cast the 0 to the right type.
Another option is to ensure the int overload is preferred, for example by making the other one a template:

class huge
{
 private:
  unsigned char data[BYTES];
 public:
  void setval(unsigned int);
  template <class T> void setval(const T *); // not implemented
  template <> void setval(const char*);
};
Bart van Ingen Schenau
  • 15,488
  • 4
  • 32
  • 41
  • For what it's worth, I had to include the specialization outside of the class declaration. Basically include `template <> void huge::setval(const char*);` just below your class declaration. If I didn't, I received an error about specializing the template in the class scope. Great trick though, this has stumped me before with some of my interfaces. – vmrob Apr 23 '14 at 01:50
  • 4
    `0` has only one meaning in C++. It is an integer literal of the type `int`. If the OP really had an overload taking an `int` and another taking a `char*`, the overload resolution would succeed and chose the `int` overload. However, OP uses `unsigned int` as the parameter type. And those two conversions, `int` -> `unsigned int` vs null pointer constant -> `char*` are ambiguous. – dyp Apr 04 '15 at 21:42
  • @dyp: If `0` has only one meaning, how can it be involved in two ambiguous conversions with different starting types? – Bart van Ingen Schenau Apr 05 '15 at 06:58
  • 1
    I don't quite understand what you mean with "starting types". The type of `0` is `int`. An `int` can be converted to an `unsigned int`. A constant expression of type `int` that evaluates to `0` can also be converted to any pointer type. For example, g++ and clang++ accept `1-1` as a valid null pointer constant (though clang++ only in C++03 mode). That is, `0` is not special, it simply fulfils the conditions to enable those two separate conversions. – dyp Apr 05 '15 at 13:26
  • What about if I cast and it still complains that it's ambiguous? I have a struct declaration with a constructor, and it claims to be ambiguous between the constructor and the actual `struct Thing {` line? – Aaron Franke Sep 16 '18 at 09:39
  • @AaronFranke: That might be worth a new question. – Bart van Ingen Schenau Sep 16 '18 at 09:51
18

The solution is very simple if we consider the type of the constant value, which should be "unsigned int" instead of "int".

Instead of:

setval(0)

Use:

setval(0u)

The suffix "u" tell the compiler this is a unsigned integer. Then, no conversion would be needed, and the call will be unambiguous.

Leonardo L.
  • 316
  • 2
  • 5
3

replace p.setval(0); with the following.

const unsigned int param = 0;
p.setval(param);

That way it knows for sure which type the constant 0 is.

Null Set
  • 5,374
  • 24
  • 37
1

Use

p.setval(static_cast<const char *>(0));

or

p.setval(static_cast<unsigned int>(0));

As indicated by the error, the type of 0 is int. This can just as easily be cast to an unsigned int or a const char *. By making the cast manually, you are telling the compiler which overload you want.

Eclipse
  • 44,851
  • 20
  • 112
  • 171
1

Cast the value so the compiler knows which function to call:

p.setval(static_cast<const char *>( 0 ));

Note, that you have a segmentation fault in your code after you get it to compile (depending on which function you really wanted to call).

Mark Loeser
  • 17,657
  • 2
  • 26
  • 34
1

That is ambiguous because a pointer is just an address, so an int can also be treated as a pointer – 0 (an int) can be converted to unsigned int or char * equally easily.

The short answer is to call p.setval() with something that's unambiguously one of the types it's implemented for: unsigned int or char *. p.setval(0U), p.setval((unsigned int)0), and p.setval((char *)0) will all compile.

It's generally a good idea to stay out of this situation in the first place, though, by not defining overloaded functions with such similar types.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
metamatt
  • 13,809
  • 7
  • 46
  • 56