MSDN has conflicting information about nullptr/__nullptr and /clr:
http://msdn.microsoft.com/en-us/library/dd465215(VS.100).aspx says:
The Visual C++ compiler lets you use the nullptr keyword with native code or with managed code. ... The compiler interprets nullptr to be managed code when you use the /clr compiler option, and native code when you do not use the /clr option.
Then adds:
The Microsoft-specific __nullptr keyword has the same meaning as nullptr, but it applies to native code only. If you compile native C/C++ code by using the /clr compiler option, the compiler cannot determine whether the nullptr keyword is a native or a managed term. To make your intention clear to the compiler, use the nullptr keyword to specify the managed term, and __nullptr to specify the native term.
Highlighted part indicates that compiler cannot determine whether it is native or managed, but the previous paragraph just said it will interpret it as managed.
http://msdn.microsoft.com/en-us/library/4ex65770.aspx says the same "cannot determine" thing and then gives an example to contrary:
The following sample shows that nullptr can be assigned to a native pointer when you compile with /clr.
// mcpp_nullptr_6.cpp
// compile with: /clr
int main() {
int * i = 0;
int * j = nullptr;
}
So what should I expect from all of this? I am guessing that the compiler takes nullptr as managed with /clr,
because otherwise there wouldn't be a way to say nullptr in managed, and the sample is just an old garbage?
Here's how it's supposed to work, and what MSDN should say:
__nullptr is always the Native Nullptr, regardless of whether the compilation is native, /clr, or /clr:pure.
std::nullptr_t is always the Native Nullptr's Type, regardless of whether the compilation is native, /clr, or /clr:pure. It is defined by <cstddef>. For good measure (as permitted but not required by C++0x), both <cstddef> and <stddef.h> define both std::nullptr_t and ::nullptr_t. JonCaves even made the compiler join the fun - it defines std::nullptr_t (but not ::nullptr_t) even in the absence of any headers whatsoever.
nullptr is the Native Nullptr when compiled as native (as required by C++0x).
nullptr is the Managed Nullptr Of Doom when compiled as /clr or /clr:pure (as required by the Necronomicon,
which is to say C++/CLI).
That said, I don't know how the Managed Nullptr works, except that I've heard that "it doesn't have a type",
a concept from which my mind recoils in fear. There's been a little talk of unifying these things,
but I don't think anybody has figured out what that would involve yet. When this came up I said:
> Regarding A Tale Of Two Nullptrs, I've heard several times that the problem is that the managed nullptr
"doesn't have a type", whatever that means, while the native nullptr has a type.
I don't know what it'd take to unify them (I assume that the native nullptr would have to "win",
since it cannot be infected with managedness, and conversions to managed stuff would have to be added to make
up the difference - and too bad for anyone who inspects nullptr closely and is surprised to discover that it
now has a type - I think all of those words in that order make sense), other than the fact that it
wasn't obviously simple enough for JonCaves to do in the first place.
> The Iron Nullptr Curtain causes more user suffering than STL suffering, though -
our only real interaction is in the definition of std::nullptr_t where we simply say decltype(__nullptr).
In the rest of our implementation, we just use NULL/0 (Dinkumware overwhelmingly prefers 0, as I recall).
In this case, perhaps the ambiguity in the docs is a reflection of the thing itself. If nullptr is always
the MNOD when compiled with /clr, then why does the compiler allow int* j to be initialized with it,
as shown in the code example referred to below? I don’t know why the docs say “the compiler cannot determine…”
because at least in these simple initializations it seems to know what to do (converting nullptr to nullptr_t
when the lhs is a native type, regardless of /clr?). But I’m curious what pain this causes or might cause for users.
Is there ever a case where the non-identity of these two things leaks out? If so, then we should really mention that
in the docs.
// mcpp_nullptr_6.cpp
// compile with: /clr
int main() {
int * i = 0;
int * j = nullptr; //what IS j now?
int^ k = nullptr; //the same or different?
}
C:\Temp>type doooooooom.cpp
#include <iostream>
#include <memory>
#include <ostream>
using namespace std;
int main() {
auto sp = make_shared<int *>(nullptr);
cout << sp.get() << endl;
}
C:\Temp>cl /EHsc /nologo /W4 doooooooom.cpp
doooooooom.cpp
C:\Temp>doooooooom
00227B2C
C:\Temp>cl /clr /nologo /W4 doooooooom.cpp
doooooooom.cpp
doooooooom.cpp(7) : error C2664: 'std::tr1::shared_ptr<_Ty> std::tr1::make_shared<int*,nullptr>(_Arg0 &&)' : cannot conv
ert parameter 1 from 'nullptr' to 'nullptr &&'
with
[
_Ty=int *,
_Arg0=nullptr
]
nullptr can only be converted to pointer or handle types
doooooooom.cpp(7) : fatal error C1903: unable to recover from previous error(s); stopping compilation