4

I have a class B with two overloaded functions int Set(B *); and int Set(const A&);. The class A expects a constructor argument unsigned char. When Set is called with a const unsigned char with value as 0, it is resolved to Set(B*) whereas when the value passed is non-zero, it resolves to Set(const A&) (as per my expectation).

The overload resolution works expectedly with non-const unsigned char but fails with const unsigned char with value set as 0. Why?

Following code illustrates the discrepancy when Set is called with const and non-const unsigned char

#include <iostream>

using namespace std;


class A{
  char m_byteValue;
public:
  A(unsigned char c) {
    m_byteValue = c;
  }
};


class B{
  int m_a;
public:
  B(){
    m_a = 2;
  }
  int Set(B *);
  int Set(const A&);
};

int B::Set(const A& v) {
  cout << "I am in the const ref function\n";
  return 0;
}

int B::Set(B* p) {
  cout << "I am in the pointer function\n";
  return 0;
}

int main(){
  const unsigned char a = 0;
  const unsigned char b = 1;
  unsigned char c = 0;
  unsigned char d = 1;
  B var;
  var.Set(a);
  var.Set(b);
  var.Set(c);
  var.Set(d);
  return 0;
}

Output (as compiled by gcc 4.9.2 c++98): Demo - on ideone c++ 5.1

I am in the pointer function // Why?
I am in the const ref function
I am in the const ref function
I am in the const ref function
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Mukul Gupta
  • 2,310
  • 3
  • 24
  • 39
  • [Unable to reproduce](http://cpp.sh/5tgd) in GCC 4.9.2 – Cory Kramer Jun 09 '16 at 11:37
  • @CoryKramer: It is reproducible in c++98. – Mukul Gupta Jun 09 '16 at 11:40
  • 1
    Hmm strange. Wonder if it's simply because there's essentially a `#DEFINE NULL 0` somewhere since this was preceding `nullptr`, so it thinks that `0` should be passed as a pointer (since it thinks you are passing `NULL`. This problem is mentioned [here](https://stackoverflow.com/questions/20509734/null-vs-nullptr-why-was-it-replaced) and [here](https://stackoverflow.com/questions/1282295/what-exactly-is-nullptr). – Cory Kramer Jun 09 '16 at 11:44
  • Added the C++98 tag as this only shows up in that version. I can't test C++03 to confirm its addition. – NathanOliver Jun 09 '16 at 11:47
  • @NathanOliver: Also, reproduced in C++03. – Mukul Gupta Jun 09 '16 at 13:35

2 Answers2

4

The difference between the standards is here:

C++98 [conv.ptr]

A null pointer constant is an integral constant expression rvalue of integer type that evaluates to zero.

C++11 [conv.ptr]

A null pointer constant is an integer literal with value zero or a prvalue of type std::nullptr_t.

const unsigned char a = 0; satisfies the C++98 definition of an integral constant expression. Of course a is not an rvalue, but it seems that an lvalue-to-rvalue conversion applies and is still a better match than the user-defined conversion from unsigned char to A.

a is not a literal, this is what makes the behaviour different in C++11.

Oktalist
  • 14,336
  • 3
  • 43
  • 63
2

Clang reproduces the error, but fortunately also gives you the answer during compilation:

overloading_incorrect.cxx:41:13: warning: expression which evaluates
to zero treated as a null pointer constant of type 'B *'
[-Wnon-literal-null-conversion] 
var.Set(a);
        ^

As NathanOliver mentions correctly under your post, this behavior disappears as soon as the c++11 standard is used (adding the -std=c++11 flag to the compiler).

Chiel
  • 6,006
  • 2
  • 32
  • 57