Question

I have this source file:

#include <ctime>

class A
{
  public:
    A(unsigned long i){};
    A(const tm*){};
};

int main(int argc, char** argv)
{
  A tit(1);
  A tat(0);
  return 0;
}

When I compile this using gcc 3.4.2 I get the following:

autoCast.cpp: In function int main(int, char**)':
autoCast.cpp:13: error: call of overloaded
A(int)' is ambiguous
autoCast.cpp:4: note: candidates are: A::A(const A&)
autoCast.cpp:7: note: A::A(const tm*)
autoCast.cpp:6: note: A::A(long unsigned int)

My question is: why does the creation of A tit(1) (line 12) succeed while A tat(0) (line 13) fails? I tried different argument types in those lines (e.g. 0ULL and 1ULL) but it always fails when the argument value is 0, while it succeeds when the argument value is 1. The only 0 value for which no error is raised is 0UL (because no conversion is needed I guess). Why in this case does an integer with value 0 get different treatment from an integer with value 1? Does it have to do with the tm struct defined in time.h?

Was it helpful?

Solution

This is because 0 is a null pointer constant and can be converted to unsigned long or a pointer and therefore it is ambiguous which to choose. The draft C++ standard section 4.10 Pointer conversions says (emphasis mine):

A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type.

While 1 is not a null pointer constant. Although 0UL can be converted to a pointer as well it is a better match for the unsigned long constructor since no conversion is needed.

OTHER TIPS

A literal 0 is often used to initialise a pointer to null (modern C++ should admittedly be using nullptr instead). However, it is comparatively uncommon to initialise pointers directly with any other integer literal (except in e.g. embedded systems which use memory mapping).

As such, the compiler is treating 0 as though it could be a pointer or an integer, meaning it could call either constructor. In contrast, it treats 1 simply as an integer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top