I'm a little alergic to #define so when I wanted to make myself a
placeholder
for nullptr keyword for C++0x compability I came up with something
like this:
const struct nullptr_t {
template<class T>
operator T*() const { return 0; }
template<class T, class U>
operator T U::*() const { return 0; }
} nullptr = {};
template<class T>
bool operator==(T* lhs, const nullptr_t rhs) { return lhs == 0; }
template<class T>
bool operator==(const nullptr_t lhs, T* rhs) { return rhs == 0; }
template<class T, class U>
bool operator==(T U::* lhs, const nullptr_t rhs) { return lhs ==
0; }
template<class T, class U>
bool operator==(const nullptr_t lhs, T U::* rhs) { return rhs ==
0; }
It does not pass only one from Basic Cases from n2431:
if( nullptr == 0 ); // error
On GCC 4.3.3 it compiles.
On MinGW-GCC 3.4.5 it crushes the compiler.
I didn't do tests for other compilers nor Advanced Cases from n2431.
Ofcourse while reading n2431 for tests I've found that this is
proposed alternate solution (library implementation instead of
keyword).
Nevertheless I thought that it could interest people as alergic go
#define as I am and who didn't read said C++0x proposal.
Cheers
Sfider
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
These are GCC issues. Comeau C++ rightly rejects that with a reasonable
error.
Indeed, all the test cases in N2431 (basic and advanced) pass with your
implementation under Comeau C++.
One minor nit. You should add a member of type "void* const" to the class,
preferably with a implementation reserved name like _Val. (Yes, this is not
allowed by the standard, but no real implementation should have issue with
that, and if anybody tries to access the member the use of such naming
should make it clear that something is wrong).
By doing that, you gain sizeof(nullptr)==sizeof(void*) as required in the
draft, without adding any other overhead.
In summary:
const struct nullptr_t {
template<class T>
operator T*() const { return 0; }
template<class T, class U>
operator T U::*() const { return 0; }
void* const _Val; //NEW
} nullptr = {};
(keep the operator== overloads of course).
Then the only issue left is the fact that nullptr_t really belongs in
namespace std.
Since (despite the standard) most compilers support users adding things to
namespace std at the slight risk of breaking things (which this would most
likely not do), I would wrap all that in a "namespace std{/*...*/}" block,
and add a "using ::std::nullptr;" statement at the end.
I think the combination of all of that simulates nullptr as closely as
possible in (real world) C++03.
#define nullptr 0
> One minor nit. You should add a member of type "void* const" to the class,
> preferably with a implementation reserved name like _Val. (Yes, this is not
> allowed by the standard, but no real implementation should have issue with
> that, and if anybody tries to access the member the use of such naming
> should make it clear that something is wrong).
I think that more proper solution would be making it private.
> Since (despite the standard) most compilers support users adding things to
> namespace std at the slight risk of breaking things (which this would most
> likely not do), I would wrap all that in a "namespace std{/*...*/}" block,
> and add a "using ::std::nullptr;" statement at the end.
I realy did not intend to simulate nullptr in such detail but it won't
hurt I suppose.
Current version looks like this:
const class nullptr_t {
public:
template<class T>
operator T*() const { return 0; }
template<class T, class U>
operator T U::*() const { return 0; }
private:
void* p;
void operator&() {}
} nullptr = nullptr_t();
nullptr should be constant (or more specifically it should be a literal)
and obtaining its address shouldn't be possible. I've also overloaded
operator!= ;).
It's a pity I cannot use it though...
Cheers
Sfider
Just curious, is there a benefit to giving it a reserved name over
giving it a descriptive name like 'unused_dummy_for_size_equivalence_'
and making it private?
- Marsh
Your nullptr still has a few semantical differences from the C++0x
equivalent per the latest draft:
if(nullptr); // implicit conversion to bool
Solution: operator bool() const { return false; }
nullptr_t(0); // conversion from integral null pointer
// constant to nullptr_t
Solution: declare a private nested (incomplete) type Z, and a ctor
accepting a pointer to Z. Unfortunately, the class will no longer
support an initializer with braces, which nullptr_t does.
reinterpret_cast<int>(nullptr); // nullptr_t can be explicitly
// converted to integral types
Solution: Seemingly impossible.
I think that's almost as close as you can get :)
Download Visual C++ Express 2008 and you'll have a much better
compiler that is also free.
With a private variable the type cannot be initalized with braces (non-POD).
But otherwise, no benefit I am aware of.
Yes, a class with private (non-static) data members is not an
aggregate (and consequently, not POD)