Proposal: null pointer class

Hello Boost developers, I'd like to propose the inclusion of a null_ptr class in Boost. While I am fully aware that this topic has been brought up several times in the past, it appears as if though all discussions slowly died off without any final decision as to whether it is a worthy candidate for inclusion in Boost or not. The null pointer class proposals I have seen before were all designed to function in a manner similar to that of the "nullptr" keyword of C++0x. That is, the classes typically provided a template conversion operator, which allows it to evaluate to any pointer type with the value zero. My approach is to make the class, rather than the operator, a template: template<typename T> class null_ptr { public: operator T*() const { return reinterpret_cast<T *>(0); } }; As we can see, passing a null_ptr requires us to explicitly specify the type of pointer. While slightly verbose, I believe that it can be highly beneficial, for the following reasons: 1. In cases where multiple overloaded functions that take pointers exist, there is no way that we could accidentally call the wrong overload (since we have to explicity specify the type). 2. It makes code easier to read and understand. When reading the code, another programmer will instantly be able to see what type argument(s) a particular function accepts, thus making it much clearer than using NULL or nullptr. As with all other null pointer classes that have been suggested to date, the primary benefit of using my null_ptr over the NULL constant is that it solves the following problem: void f(int) { ... } void f(char *) { ... } int main() { func(NULL); func(null_ptr<char>()); } When passing NULL, f(int) will be called -- probably not what the programmer intended. The second call utilizes null_ptr<char> to guarantee that the overload of f() taking char* will be called instead of the one taking int. As noted above, my null_ptr class also handles multiple overloads taking pointer types gracefully. Please let me know what you think! P.S: I have tested the code on Microsoft Visual C++ 2008 as well as gcc 4.3.3 (Linux). Regards, Martin Törnwall

Martin Törnwall wrote:
Hello Boost developers,
I'd like to propose the inclusion of a null_ptr class in Boost. template<typename T> class null_ptr { public: operator T*() const { return reinterpret_cast<T *>(0); } };
Why not make it even simpler and more general. template <typename T> T null() {return T(); } int main() { func(NULL); func(null<char*>()); } It also gives the null string or null value of any other type that default initializes to its null value. I'm pretty sure the default initialized pointer value is null according to the standard, so this should be portable, but I am not an expert on the standard. Regards, Luke

AMDG Simonson, Lucanus J wrote:
I'm pretty sure the default initialized pointer value is null according to the standard, so this should be portable, but I am not an expert on the standard.
Err... you mean value initialized, not default initialized, right? In Christ, Steven Watanabe

Steven Watanabe wrote:
AMDG
Simonson, Lucanus J wrote:
I'm pretty sure the default initialized pointer value is null according to the standard, so this should be portable, but I am not an expert on the standard.
Err... you mean value initialized, not default initialized, right?
Uh, whatever explicitly calling the default constructor is called. I know that declaring a pointer leaves it in uninitialized state whereas calling the default constructor of a pointer initializes it to null, but I guess my terminology is lacking. Regards, Luke

Simonson, Lucanus J On Monday, June 15, 2009 3:21 PM
Martin Törnwall wrote:
I'd like to propose the inclusion of a null_ptr class in Boost. template<typename T> class null_ptr { public: operator T*() const { return reinterpret_cast<T *>(0); } };
reinterpret_cast isn't needed here. Just return 0.
Why not make it even simpler and more general.
template <typename T> T null() {return T(); }
int main() { func(NULL); func(null<char*>()); }
Presumably, you meant for null() to return T *. However, that doesn't address the overloading ambiguities that can result when overloading on int and char *, for example. That's why a new type is needed. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Stewart, Robert wrote:
Why not make it even simpler and more general.
template <typename T> T null() {return T(); }
int main() { func(NULL); func(null<char*>()); ^^^^^
Presumably, you meant for null() to return T *. However, that doesn't address the overloading ambiguities that can result when overloading on int and char *, for example. That's why a new type is needed.
No, T is char* in my example. I meant the code to do exactly what I wrote. This does address the problem of pointer type overloading since you specify the pointer type and it returns the null value of that pointer type. Regards, luke

Simonson, Lucanus J On Monday, June 15, 2009 3:42 PM
Stewart, Robert wrote:
Why not make it even simpler and more general.
template <typename T> T null() {return T(); }
int main() { func(NULL); func(null<char*>()); ^^^^^
Presumably, you meant for null() to return T *. However, that doesn't address the overloading ambiguities that can result when overloading on int and char *, for example. That's why a new type is needed.
No, T is char* in my example. I meant the code to do exactly what I wrote. This does address the problem of pointer type overloading since you specify the pointer type and it returns the null value of that pointer type.
I misunderstood your intent. By returning a pointer type, you do eliminate the ambiguity. Note that your function template can be misused because T isn't necessarily a pointer type. (That can be added, of course.) With that fixed, your function should simply return 0 rather than T(). _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Stewart, Robert wrote:
Simonson, Lucanus J
template <typename T> T null() {return T(); }
int main() { func(NULL); func(null<char*>());
Note that your function template can be misused because T isn't necessarily a pointer type. (That can be added, of course.) With that fixed, your function should simply return 0 rather than T().
Martin Törnwall wrote:
You make a good point, but the purpose of the class I proposed is very specific -- that of solving problems related to the use of the NULL pointer constant. I believe that it could be somewhat confusing to have a "null" template function that returns both null pointers and objects initialized with the default constructor.
Why? T() is just as portable and there is no need to prevent someone from calling null<string>() or null<char>() because such uses are functionally correct and clear in intent. Lets say we wanted to implement a null terminated sentinel iterator adaptor and make it default to use null<T> if no sentinel value is provided. That would be reasonable to do because you could then specialize null<T> for your type that doesn't provide default constructor instead of having to specify the terminator in the sentinel iterator adaptor every time you use it. Is it confusing that a template named null returns the null value of its parameter and needs to be specialized for types that don't default construct to their null value? All that said, I'm not sure how much we need this in boost. In my own code I would just use a C-style case in such cases, or reinterpret_cast<char*>(0) if C-style casts were frowned upon by offical style policies. You can't call char*() because it is a syntax error but typedef char* charp; charp() is legal syntax. I template was just doodling with a way to call the pointer default constructor as directly as possible without generating syntax errors. (char*)0 is about the most succinct you can get, but regardless of what you do, you still have an error prone overload because it is up to the user to remember to cast null to the desired pointer type. Since the problem with pointer type overloads and null value is that it is error prone, and not that it is hard to do, I don't think trying to make it easier is solving the real problem. All you can do is avoid defining such overloads in the first place. Regards, Luke

Lucanus Simonson wrote:
All that said, I'm not sure how much we need this in boost. In my own code I would just use a C-style case in such cases, or reinterpret_cast<char*>(0) if C-style casts were frowned upon by offical style policies. You can't call char*() because it is a syntax error but typedef char* charp; charp() is legal syntax. I template was just doodling with a way to call the pointer default constructor as directly as possible without generating syntax errors. (char*)0 is about the most succinct you can get, but regardless of what you do, you still have an error prone overload because it is up to the user to remember to cast null to the desired pointer type. Since the problem with pointer type overloads and null value is that it is error prone, and not that it is hard to do, I don't think trying to make it easier is solving the real problem. All you can do is avoid defining such overloads in the first place. But a templated null pointer class also makes the code more clear, in my opinion. When passing null pointers as arguments to functions we can see, by merely looking at the call, what parameters the function expects to receive, which might give an indication of what it actually does.

Martin Törnwall wrote:
But a templated null pointer class also makes the code more clear, in my opinion. When passing null pointers as arguments to functions we can see, by merely looking at the call, what parameters the function expects to receive, which might give an indication of what it actually does.
Foo(0); Foo(NULL); Foo((char*)0); Foo(reinterpret_cast<char*>(0)); Foo(null_ptr<char>()); Foo(null<char*>()); Of all of these only the first two are unclear. The rest are just different ways of doing (char*)0 by typing more. At best you are saving ten keystrokes over reinterpret_cast<char*>(0). Unless you do this quite a lot I don't think we'll ever save more keystrokes than we've spent discussing it, since I find myself passing by reference most of the time because people usually don't want to worry null pointers passed into their API. I think (char*)0 is perfectly fine because it is in no way ambiguous what it does. C-style casts for C-sytle types is my practice. For those who insist on typing more, do we really need a library solution? Please note that your library solution doesn't force people to use it to document their pointer type in the code, so it doesn't really solve this problem either, since people can forget to use it. If you did this: template <typename T> class cruel_ptr { private: T* v; null_ptr(int); //not defined and illegal public: null_ptr(T* t) v(t) {} operator T*() const { return v; } }; then you could use it to prevent people from passing literal constant null values into your functions expecting a pointer type, but I'm not sure such would really be considered helpful by users of the API, and wouldn't solve the problem of legacy functions that take pointer arguments. Regards, Luke

The name "null" is not proper for all these non-pointer types (or typed for which the default initializer does not happen to "null" its state, whatever that means for more advanced types.) The name that best describes the semantics here is "defaulted". I do like the idea of not requiring strict pointer types, and thus keeping the notion open for smart pointers (and Pimples) but I think we are then degressing.. /David Typed on an iPhone On Jun 15, 2009, at 4:20 PM, "Simonson, Lucanus J" <lucanus.j.simonson@intel.com
wrote:
Stewart, Robert wrote:
Simonson, Lucanus J
template <typename T> T null() {return T(); }
int main() { func(NULL); func(null<char*>());
Note that your function template can be misused because T isn't necessarily a pointer type. (That can be added, of course.) With that fixed, your function should simply return 0 rather than T().
Martin Törnwall wrote:
You make a good point, but the purpose of the class I proposed is very specific -- that of solving problems related to the use of the NULL pointer constant. I believe that it could be somewhat confusing to have a "null" template function that returns both null pointers and objects initialized with the default constructor.
Why? T() is just as portable and there is no need to prevent someone from calling null<string>() or null<char>() because such uses are functionally correct and clear in intent. Lets say we wanted to implement a null terminated sentinel iterator adaptor and make it default to use null<T> if no sentinel value is provided. That would be reasonable to do because you could then specialize null<T> for your type that doesn't provide default constructor instead of having to specify the terminator in the sentinel iterator adaptor every time you use it. Is it confusing that a template named null returns the null value of its parameter and needs to be specialized for types that don't default construct to their null value?
All that said, I'm not sure how much we need this in boost. In my own code I would just use a C-style case in such cases, or reinterpret_cast<char*>(0) if C-style casts were frowned upon by offical style policies. You can't call char*() because it is a syntax error but typedef char* charp; charp() is legal syntax. I template was just doodling with a way to call the pointer default constructor as directly as possible without generating syntax errors. (char*)0 is about the most succinct you can get, but regardless of what you do, you still have an error prone overload because it is up to the user to remember to cast null to the desired pointer type. Since the problem with pointer type overloads and null value is that it is error prone, and not that it is hard to do, I don't think trying to make it easier is solving the real problem. All you can do is avoid defining such overloads in the first place.
Regards, Luke _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Stewart Robert wrote:
reinterpret_cast isn't needed here. Just return 0.
Thank you for pointing that out. I threw it in in case some obscure compiler would complain with a warning. Lucanus Simonson wrote:
Why not make it even simpler and more general.
template <typename T> T null() {return T(); }
int main() { func(NULL); func(null<char*>()); }
It also gives the null string or null value of any other type that default initializes to its null value. I'm pretty sure the default initialized pointer value is null according to the standard, so this > should be portable, but I am not an expert on the standard.
You make a good point, but the purpose of the class I proposed is very specific -- that of solving problems related to the use of the NULL pointer constant. I believe that it could be somewhat confusing to have a "null" template function that returns both null pointers and objects initialized with the default constructor. Regards, Martin Törnwall

2009/6/15 Martin Törnwall <herede@tngemu.com>:
int main() { func(NULL); func(null_ptr<char>()); }
When passing NULL, f(int) will be called -- probably not what the programmer intended. The second call utilizes null_ptr<char> to guarantee that the overload of f() taking char* will be called instead of the one taking int. As noted above, my null_ptr class also handles multiple overloads taking pointer types gracefully. Please let me know what you think!
I think that the advantage of nullptr is not needing to specify the class -- I'm not convinced that I'd actually bother #including something so I can type the longer null_ptr<T>() instead of (T*)0.
participants (6)
-
David Bergman
-
Martin Törnwall
-
Scott McMurray
-
Simonson, Lucanus J
-
Steven Watanabe
-
Stewart, Robert