views:

175

answers:

6

Hello all,

I saw the following code:

class NullClass {
public:
    template<class T> operator T*() const { return 0; }
};

const NullClass NULL;

void f(int x);
void f(string *p);

f(NULL); // converts NULL to string*, then calls f(string*)

Q1> I have problems to understand the following statement

template<class T> operator T*() const { return 0; }

Specially, what is the meaning of operator T*()?

Q2> Why f(NULL) is finally triggering the f(string*)?

Thank you

+2  A: 

template<class T> operator T*() means that there is an implicit conversion from NullClass to T* for any T.

When you're calling f(NULL), the compiler needs to decide which overload to use. Since neither overload takes an object of type NullClass, it looks which implicit conversions exist. There is no conversion to int, but there is one to string*, so the conversion is applied and the string* overload is called.

sepp2k
+11  A: 

what is the meaning of operator T*()?

It is a user-defined conversion operator. It allows an object of type NullClass to be converted to any pointer type.

Such conversion operators can often lead to subtle, unexpected, and pernicious problems, so they are best avoided in most cases (they are, of course, occasionally useful).

Why f(NULL) is finally triggering the f(string*)?

NULL is of type NullClass. It can't be converted to an int, but the user-defined conversion NullClass -> T* can be used to convert it to a string*, so void f(string*) is selected.

Note that this works because there is only one overload of f that takes a pointer. If you had two overloads,

void f(int*);
void f(float*);

the call would be ambiguous because the NullClass -> T* conversion can be converted to both int* and float*.

James McNellis
Note that this ambiguity would also exist when using the traditional `NULL` value: `0`.
sbi
Hi James, I like what you mentioned "NULL is of type NullClass. It can't be converted to an int"! -- thank you
q0987
+2  A: 
  1. operator Anything() overloads the "cast" operator. Whenever NullClass needs to be converted to Anything, this function will be called, and the result will be used.

    In your case, Anything is T* where T can be any type (it is a template parameter), meaning a NullClass supports casting to any pointers.

  2. Since NullClass can be cast into any pointers, including a string*. So the f(string*) version will be used.

KennyTM
+1  A: 

The NuLLClass provides a conversion function to convert to a pointer. When you call f(NULL), it will try to find a way to convert NULL to a valid argument for f.

Because you can call operator T*() on NULL, it will with T=string. This fulfills the demand for f(string *). Since there is no way to convert NULL to an int, there is only one clear choice of which function to call.

JoshD
+1  A: 

Specially, what is the meaning of operator T*()?

It is a conversion operator to type T*. It allows operations such as (T*)NULL.

Q2> Why f(NULL) is finally triggering the f(string*)?

Because the compiler looks for the best match between the arguments and the method's signature, in this case choosing template<typename T> NullClass::operator T*(), where T=string.

André Caron
+2  A: 

Q1> I have problems to understand the following statement

template<class T> operator T*() const { return 0; }

Specially, what is the meaning of operator T*()?

It's an implicit conversion operator. It makes it possible to have objects of the type it belongs to implicit convert to the target type T* here. This version is a special one, since, being a template, it can convert an object of that NullClass to any pointer type.
Implicit conversion are frowned upon for good reasons. They have the bad habit of kicking in at unexpected moments, making the compiler call an unintended version of an overloaded function. Having a templatized implicit conversion operator is especially evil because templatization multiplies the possibilities.

Q2> Why f(NULL) is finally triggering the f(string*)?

See above. There is no possible conversion to int, so the implicit conversion operator kicks in and converts an NullClass object into any pointer requested.
I suppose this is intended. Usually you don't want a pointer to be converted to an integer, which is why that class has an implicit conversion into any pointer, but none to int.


That NullClass isn't all that bad, but the NULL instance of it is pure stupidity. As soon as you include any of the many headers that define the NULL macro (defined to be 0, i.e. an integer constant), the preprocessor will trample over all source and replace each usage of NULL with 0. Since you can't avoid to include this, this error renders the whole class pretty much useless.

sbi