x6herbius x6herbius - 2 months ago 6
C++ Question

Cannot convert NULL to a pointer-to-object type

I've not been able to find an explanation of the following on Google so far, and it's confusing me a little.

I have a

Scene
which stores hierarchies of
SceneObjects
. The
Scene
acts as a templated
SceneObject
factory, so that bookkeeping can be done when a
SceneObject
subclass instance is created or deleted. Both of these classes are in a dynamically linked module of their own and are within a module namespace (not sure whether this matters).

The (simplified)
SceneObject
class looks like this:

// SceneObject is a base class, but is not pure.
class SceneObject
{
// The parent Scene needs to be able to access protected functions.
friend class Scene;

protected:
// This is protected to enforce the factory design pattern.
// We don't want SceneObjects created without being tied to a Scene.
SceneObject(Scene* parentScene, SceneObject* parentObject);

public:
...
};


And the (simplified)
Scene
class looks like this:

class Scene
{
public:

// General bookkeeping - a fully constructed subclass is required
// here as the subclass constructor sets certain member variables.
void processSceneObjectCreated(SceneObject* object);

// This function means we can do:
// SceneObjectSub* obj = scene.createSceneObject<SceneObjectSub>(...)
// and pass whatever parameters are required for that particular
// subclass' constructor, while ensuring the Scene can keep a record
// of the created object.
//
// We can't call processSceneObjectCreated() in the SceneObject base
// class' constructor, as the required subclass constructor will not
// have been run yet.
template<typename T, typename... Args>
T* createSceneObject(Args... args)
{
T* obj = new T(this, std::move(args)...);
processSceneObjectCreated(obj);
return obj;
}

...
};


As a test, I compiled the following code to create a new
SceneObject
:

ModuleNS::Scene scene;
ModuleNS::SceneObject* sceneObject =
scene.createSceneObject<ModuleNS::SceneObject>(NULL);


However, the MSVC compiler gave me the following error:


Cannot convert argument 2 from 'int' to 'ModuleNS::SceneObject*'


This confused me, as I thought
NULL
(ie. 0) could always be converted to a pointer type. If instead I use
static_cast<ModuleNS::SceneObject*>(NULL)
, or
nullptr
(which I'd like to use, but for the sake of consistency with old code I've been using
NULL
instead), the compile error goes away.

What specifically causes NULL to stop being castable to a pointer?

AnT AnT
Answer

The error has the same nature as the one in the following sample

template <typename T> void foo(T t) {
    void *p = t; // ERROR here
}

int main() {
    foo(NULL);
}

In C++ only literal 0 can be converted to pointer type to produce null-pointer value. NULL expands to a literal zero, which is why you can use it to initialize/assign/compare with pointers directly. "Directly" is the key word here.

But once you feed it through a function parameter, it is no longer a literal 0 and can no longer act as a null-pointer constant.

In the above example T is deduced as some integer type. Inside the function t is just a [run-time] integer that happens to have value 0. And you are not allowed to initialize pointers with arbitrary integers.

Note that in modern C++ NULL can actually be defined as nullptr, which will make the above code to compile. But with the "traditional" definition of NULL (as integral 0) it won't.

Comments