Cast free NULLs in algorithms

Hello, I have something like: class A; boost::array<A*,10> the_array; std::fill( the_array.begin(), the_array.end(), NULL ); Which wont compile (at least under Visual Studio 2005) without a cast for NULL... I've had this problem before and decided to try to find an easy cast free expressive solution for it. After a few minutes I came up with: namespace detail { struct null_ptr_impl { template< typename T > operator T* () const { return static_cast<T*>(0); } }; } // end namespace detail const detail::null_ptr_impl null_ptr; std::fill( the_array.begin(), the_array.end(), null_ptr ); Does this exist somewhere already? Is it a good solution? Thanks, Michael Marcin

This was a proposed library implementation of nullptr for c++0x from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf const // this is a const object... class { public: template<class T> // convertible to any type operator T*() const // of null non-member { return 0; } // pointer... template<class C, class T> // or any type of null operator T C::*() const // member pointer... { return 0; } private: void operator&() const; // whose address can't be taken } nullptr = {}; // and whose name is nullptr Very similar to your impl although I think the static_cast in yours is redundant. The standard says, 0, being an integral constant expression that evaluates to zero, is a null pointer constant which is implicitly convertible to a pointer type (4.10) Chris On 7/31/07, Michael Marcin <mike@mikemarcin.com> wrote:
Hello,
I have something like:
class A; boost::array<A*,10> the_array;
std::fill( the_array.begin(), the_array.end(), NULL );
Which wont compile (at least under Visual Studio 2005) without a cast for NULL... I've had this problem before and decided to try to find an easy cast free expressive solution for it.
After a few minutes I came up with:
namespace detail { struct null_ptr_impl { template< typename T > operator T* () const { return static_cast<T*>(0); } }; } // end namespace detail const detail::null_ptr_impl null_ptr;
std::fill( the_array.begin(), the_array.end(), null_ptr );
Does this exist somewhere already? Is it a good solution?
Thanks,
Michael Marcin
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Chris Fairles wrote:
This was a proposed library implementation of nullptr for c++0x from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf
const // this is a const object... class { public: template<class T> // convertible to any type operator T*() const // of null non-member { return 0; } // pointer... template<class C, class T> // or any type of null operator T C::*() const // member pointer... { return 0; } private: void operator&() const; // whose address can't be taken } nullptr = {}; // and whose name is nullptr
Very similar to your impl although I think the static_cast in yours is redundant. The standard says, 0, being an integral constant expression that evaluates to zero, is a null pointer constant which is implicitly convertible to a pointer type (4.10)
Cute. It might be nice to have this as a Boost utility library. Thanks for the link, Michael Marcin

Michael Marcin wrote:
Chris Fairles wrote:
This was a proposed library implementation of nullptr for c++0x from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf
const // this is a const object... class { public: template<class T> // convertible to any type operator T*() const // of null non-member { return 0; } // pointer... template<class C, class T> // or any type of null operator T C::*() const // member pointer... { return 0; } private: void operator&() const; // whose address can't be taken } nullptr = {}; // and whose name is nullptr
Very similar to your impl although I think the static_cast in yours is redundant. The standard says, 0, being an integral constant expression that evaluates to zero, is a null pointer constant which is implicitly convertible to a pointer type (4.10)
Cute. It might be nice to have this as a Boost utility library.
Interestingly that implementation fails for my test case because: [temp.arg.type] 14.3.1 Template type arguments 2 A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template type-parameter. So it must be named to be useful as an argument to say std::fill. - Michael Marcin

Hello Chris, Tuesday, July 31, 2007, 10:22:51 PM, you wrote:
This was a proposed library implementation of nullptr for c++0x from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf
Hmm, it seems it doesn't convert to function or member function pointers... -- Best regards, Andrey mailto:andysem@mail.ru

Andrey Semashev wrote:
Hello Chris,
Tuesday, July 31, 2007, 10:22:51 PM, you wrote:
This was a proposed library implementation of nullptr for c++0x from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf
Hmm, it seems it doesn't convert to function or member function pointers...
It can't because of limitations of the syntax for defining conversion operators. See the discussion for core issue #395: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#395 -- Eric Niebler Boost Consulting www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

Hello Eric, Wednesday, August 1, 2007, 10:32:04 PM, you wrote:
Andrey Semashev wrote:
Hello Chris,
Tuesday, July 31, 2007, 10:22:51 PM, you wrote:
This was a proposed library implementation of nullptr for c++0x from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf
Hmm, it seems it doesn't convert to function or member function pointers...
It can't because of limitations of the syntax for defining conversion operators. See the discussion for core issue #395:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#395
But if nullptr is a built in keyword and its decltype is supported by the compiler itself, there would be no need to describe it in C++ syntax. -- Best regards, Andrey mailto:andysem@mail.ru

Andrey Semashev wrote:
Hello Chris,
Tuesday, July 31, 2007, 10:22:51 PM, you wrote:
This was a proposed library implementation of nullptr for c++0x from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf
Hmm, it seems it doesn't convert to function or member function pointers...
On what modern compiler does it not?

Hello Peter, Wednesday, August 1, 2007, 10:34:15 PM, you wrote:
Andrey Semashev wrote:
Hello Chris,
Tuesday, July 31, 2007, 10:22:51 PM, you wrote:
This was a proposed library implementation of nullptr for c++0x from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf
Hmm, it seems it doesn't convert to function or member function pointers...
On what modern compiler does it not?
I was referring to the built in nullptr keyword proposal quoted above. I'm not aware of any compilers that support it now. Maybe GCC 4.3.0? -- Best regards, Andrey mailto:andysem@mail.ru

Andrey Semashev wrote:
Hello Peter,
Wednesday, August 1, 2007, 10:34:15 PM, you wrote:
Andrey Semashev wrote:
Hello Chris,
Tuesday, July 31, 2007, 10:22:51 PM, you wrote:
This was a proposed library implementation of nullptr for c++0x from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf
Hmm, it seems it doesn't convert to function or member function pointers...
On what modern compiler does it not?
I was referring to the built in nullptr keyword proposal quoted above.
'nullptr' also converts to both function pointers and pointers to member functions.

Hello Peter, Wednesday, August 1, 2007, 11:14:43 PM, you wrote:
Hmm, it seems it doesn't convert to function or member function pointers...
On what modern compiler does it not?
I was referring to the built in nullptr keyword proposal quoted above.
'nullptr' also converts to both function pointers and pointers to member functions.
Ah, right. I mistook "pointer-to-member type" as a pointer to _data_ member. The Standard seems to mean both data and function members by this. -- Best regards, Andrey mailto:andysem@mail.ru

Peter Dimov wrote:
Andrey Semashev wrote:
Hello Chris,
Tuesday, July 31, 2007, 10:22:51 PM, you wrote:
This was a proposed library implementation of nullptr for c++0x from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf Hmm, it seems it doesn't convert to function or member function pointers...
On what modern compiler does it not?
Interesting. I guess the conversion to member pointer also works for member function pointers on compliant compilers. I notice that the following program doesn't compile with gcc 3.4 though: struct nullptr_t { template<typename T> operator T*() const { return 0; } template<typename T, typename U> operator T U::*() const { return 0; } }; const nullptr_t nullptr = {}; struct A {}; int main() { A *p1 = nullptr; void (*p2)() = nullptr; int (A::*p3) = nullptr; int (A::*p4)() = nullptr; } -- Eric Niebler Boost Consulting www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

Hello Eric, Wednesday, August 1, 2007, 11:17:12 PM, you wrote:
Interesting. I guess the conversion to member pointer also works for member function pointers on compliant compilers. I notice that the following program doesn't compile with gcc 3.4 though:
struct nullptr_t { template<typename T> operator T*() const { return 0; }
template<typename T, typename U> operator T U::*() const { return 0; } };
const nullptr_t nullptr = {};
struct A {};
int main() { A *p1 = nullptr; void (*p2)() = nullptr; int (A::*p3) = nullptr; int (A::*p4)() = nullptr; }
I'm surprised too. I always though that T U::* and T (U::*)() are very different types. I didn't find in the Standard any notes that they are the same. -- Best regards, Andrey mailto:andysem@mail.ru

Andrey Semashev wrote:
I'm surprised too. I always though that T U::* and T (U::*)() are very different types. I didn't find in the Standard any notes that they are the same.
A common misconception invented by the authors of GCC, I guess ;-). With MSVC, the type expression 'U C::*' deduced from a member function pointer T (with default options) will surprisingly yields a type incompatible to T (as member functions are implicitly __thiscall attributed, where U C::* becomes __cdecl, because U is the type of a freestanding function and __thiscall only exists for member functions). Regards, Tobias

on Wed Aug 01 2007, Andrey Semashev <andysem-AT-mail.ru> wrote:
I always though that T U::* and T (U::*)() are very different types. I didn't find in the Standard any notes that they are the same.
They're not. But T U::* can be the same as X (U::*)() if T is X() Yes, this surprised me too when I learned it. :) -- Dave Abrahams Boost Consulting http://www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

Hello Mathias, Wednesday, August 1, 2007, 10:50:12 PM, you wrote:
Andrey Semashev wrote:
Hmm, it seems it doesn't convert to function or member function pointers...
Since when is NULL accepted for those?
It always did. It's often used in safe bool idiom (see Boost.Optional for example). -- Best regards, Andrey mailto:andysem@mail.ru

Michael Marcin wrote:
Hello,
I have something like:
class A; boost::array<A*,10> the_array;
std::fill( the_array.begin(), the_array.end(), NULL );
Which wont compile (at least under Visual Studio 2005) without a cast for NULL... I've had this problem before and decided to try to find an easy cast free expressive solution for it.
I'm quite confused about what this whole thread is all about. I would simply write std::fill(the_array.begin(), the_array.end(), 0); and move on. What am I missing ? Thanks, Stefan -- ...ich hab' noch einen Koffer in Berlin...

Stefan Seefeld wrote:
Michael Marcin wrote:
Hello,
I have something like:
class A; boost::array<A*,10> the_array;
std::fill( the_array.begin(), the_array.end(), NULL );
Which wont compile (at least under Visual Studio 2005) without a cast for NULL... I've had this problem before and decided to try to find an easy cast free expressive solution for it.
I'm quite confused about what this whole thread is all about. I would simply write
std::fill(the_array.begin(), the_array.end(), 0);
and move on. What am I missing ?
Does this compile for you?

Stefan Seefeld wrote:
Michael Marcin wrote:
Hello,
I have something like:
class A; boost::array<A*,10> the_array;
std::fill( the_array.begin(), the_array.end(), NULL );
Which wont compile (at least under Visual Studio 2005) without a cast for NULL... I've had this problem before and decided to try to find an easy cast free expressive solution for it.
I'm quite confused about what this whole thread is all about. I would simply write
std::fill(the_array.begin(), the_array.end(), 0);
and move on. What am I missing ?
I think the issue is that, although the literal "0" can be implicitly converted to a pointer, when passed to std::fill(), its type is deduced to be "int", at which point it can no longer be converted to a pointer. -- Eric Niebler Boost Consulting www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

Eric Niebler wrote:
I think the issue is that, although the literal "0" can be implicitly converted to a pointer, when passed to std::fill(), its type is deduced to be "int", at which point it can no longer be converted to a pointer.
I see ! I hadn't realized that the third parameter type was deduced from the argument. (Instead I was fixated on the use of 'NULL' instead of '0'.) Thanks, Stefan -- ...ich hab' noch einen Koffer in Berlin...

On 31/07/07, Stefan Seefeld <seefeld@sympatico.ca> wrote:
I see ! I hadn't realized that the third parameter type was deduced from the argument. (Instead I was fixated on the use of 'NULL' instead of '0'.)
And NULL is, of course, #defined to 0. On a slightly off-topic but perhaps more interesting note, what's the rationale for having a separate template parameter for the type of the initializing element? If it used the iterator's value_type (or a reference thereto) then this thread wouldn't be happening. Though to answer my own question, I suppose that the current one is more general and the cast can be added by hand fairly easily, and going the other way isn't possible. It seems like filling with 0 is common enough that there should perhaps be a std::reset algorithm that sets them all to a default-constructed value. It'd be nice and easy to optimize to memsets for PODs, too... ~ Scott

On a slightly off-topic but perhaps more interesting note, what's the rationale for having a separate template parameter for the type of the initializing element? If it used the iterator's value_type (or a reference thereto) then this thread wouldn't be happening.
I think it's because std::iterator_traits didn't exist when the STL was first designed. It's similar with accumulate(), you have to pass in the starting value so it can grab the type. -Lewis

on Wed Aug 01 2007, Lewis Hyatt <lhyatt-AT-princeton.edu> wrote:
On a slightly off-topic but perhaps more interesting note, what's the rationale for having a separate template parameter for the type of the initializing element? If it used the iterator's value_type (or a reference thereto) then this thread wouldn't be happening.
Good point. I guess the current way allows more mischief, which might be intentional. For example, you could pass an object that, every time it is converted to int, produces a new random value.
I think it's because std::iterator_traits didn't exist when the STL was first designed. It's similar with accumulate(), you have to pass in the starting value so it can grab the type.
You pass the starting value to accumulate so it can grab the value, not the type. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

David Abrahams wrote:
on Wed Aug 01 2007, Lewis Hyatt <lhyatt-AT-princeton.edu> wrote:
I think it's because std::iterator_traits didn't exist when the STL was first designed. It's similar with accumulate(), you have to pass in the starting value so it can grab the type.
You pass the starting value to accumulate so it can grab the value, not the type.
Supplying a wider type to avoid overflow can be useful.
participants (12)
-
Andrey Semashev
-
Chris Fairles
-
David Abrahams
-
Eric Niebler
-
Lewis Hyatt
-
Mathias Gaunard
-
Michael Marcin
-
Michael Marcin
-
Peter Dimov
-
Scott McMurray
-
Stefan Seefeld
-
Tobias Schwinger