Topic: nullptr (was shared_ptr and nullptr)


Author: musiphil@bawi.org (Seungbeom Kim)
Date: Fri, 24 Nov 2006 21:06:38 GMT
Raw View
Peter Dimov wrote:
> Seungbeom Kim wrote:
>
>> Personally, I have found the plain 0 satisfactory for most cases. It was
>> only in very rare cases, such as vararg functions like the execl*()
>> family, that I had to resort to static_cast, in which case nullptr won't
>> work anyway because
>> 1. static_cast<int*>(0) and static_cast<char*>(0) may have different
>> representations, and
>> 2. the type cannot be deduced because of the ambiguity.
>
> Repost:
>
> std::vector< int* > v;
>
> std::fill( v.begin(), v.end(), 0 ); // fails
> std::remove( v.begin(), v.end(), 0 ); // fails
>
> Here's one more:
>
> void f( int* );
>
> boost::bind( f, 0 )(); // fails

Yes, you're right. There are those cases. I must have either had no such
experience, or forgotten some because I tend to substitute static_cast
instantly without much thinking when the plain 0 causes an error. :)

After thinking again about the real cause and possible solutions of the
problem, the nullptr proposal doesn't seem as unsatisfactory as it used
to. (Except that it's too long and I might most likely be still using
the plain 0 where it's okay or feel tempted to #define null nullptr.)
Thanks for providing me with a chance to gain more insight on this.

--
Seungbeom Kim

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Fri, 24 Nov 2006 19:27:24 CST
Raw View
"Joe Gottman" wrote:
> [Repost: the first one was lost]
>
> "Seungbeom Kim" <musiphil@bawi.org> wrote in message
> news:ek18gj$kda$1@news.Stanford.EDU...
> > Joe Gottman wrote:
> >>>
> >>> foo(static_cast<int*>(0));
> >>> foo(static_cast<char*>(0));
> >>>
> >>> (By the way, this is one of the reasons why I'm sceptical about the
> >>> nullptr proposal; you cannot go without specifying the type completely.)
> >>
> >> Making users call foo(static_cast<int *>) is ugly.
> >
> > Yes, but it's what it is, and attempts to hide the ugliness hasn't
> > seemed very satisfactory to me: there are cases when you have to specify
> > the type explicitly.
> >
> >    void foo(int*);
> >    void foo(char*);
> >    foo(nullptr);  // won't work: call which one?
> >
>    You edited out the important fact that I defined an extra overload
>     void foo(nullptr_t);
>
> If we define this function, then foo(nullptr) calls foo(nullptr_t), no
> matter what other functions or template functions are defined.

But a nullptr_t function overload would not be useful since it matches
only a null pointer constant argument - and not every argument that has
a null pointer value.

       void f( const char *);
       void f( nullptr_t);

       const char * p = nullptr;

       f( p ); // calls f( const char *);
       f( nullptr ); // calls f( nullptr_t);

The caller is naturally expecting that the two calls to f() behave
identically, since the argument is the same in both calls. But since
each call to f() is actually dispatched to a different implementation
of f(), the program has created a maintenance problem for itself by
distributing identical function calls between two different
implementations.

Furthermore, the fact that f() accepts a pointer means that a null
pointer argument should be acceptable input to f(). Therefore, f()
should have no reason to divert null pointer arguments to some other
routine instead of handling that kind of input itself. If a null
pointer argument is not acceptable as argument to f(), then f() would
be expected to have declared a (possibly const) reference parameter
instead of a pointer.

Greg

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Sat, 25 Nov 2006 20:36:09 GMT
Raw View
Greg Herlihy ha scritto:
> "Joe Gottman" wrote:
>> "Seungbeom Kim" <musiphil@bawi.org> wrote in message
>>>
>>>    void foo(int*);
>>>    void foo(char*);
>>>    foo(nullptr);  // won't work: call which one?
>>>
>>    You edited out the important fact that I defined an extra overload
>>     void foo(nullptr_t);
>>
>> If we define this function, then foo(nullptr) calls foo(nullptr_t), no
>> matter what other functions or template functions are defined.
>
> But a nullptr_t function overload would not be useful since it matches
> only a null pointer constant argument - and not every argument that has
> a null pointer value.
>
>        void f( const char *);
>        void f( nullptr_t);

This example is essentially different from the one under discussion,
where you would have three overloads (two different pointer types plus
nullptr_t). In that case, the nullptr_t overload is useful to
disambiguate, while in your example it's useless because there was no
ambiguity in the first place.

>
>        const char * p = nullptr;
>
>        f( p ); // calls f( const char *);
>        f( nullptr ); // calls f( nullptr_t);
>
> The caller is naturally expecting that the two calls to f() behave
> identically, since the argument is the same in both calls.

In the "three overloads" example, you couldn't say the caller expects
these two calls to behave identically:

  int* pi = nullptr;
  char* pc = nullptr;
  foo(pi);  // calls foo(int*)
  foo(pc);  // calls foo(char*)

> identically, since the argument is the same in both calls. But since
> each call to f() is actually dispatched to a different implementation
> of f(), the program has created a maintenance problem for itself by
> distributing identical function calls between two different
> implementations.

I expect the nullptr_t overload to simply disambiguate, for example:

  inline void foo(nullptr_t) { foo((char*)0); }

I see no real maintenance problem with that.

> Furthermore, the fact that f() accepts a pointer means that a null
> pointer argument should be acceptable input to f(). Therefore, f()
> should have no reason to divert null pointer arguments to some other
> routine instead of handling that kind of input itself. If a null
> pointer argument is not acceptable as argument to f(), then f() would
> be expected to have declared a (possibly const) reference parameter
> instead of a pointer.

Agreed, the f() in your example have no reason to divert null pointer
arguments to some other routine. But the foo(int*)/foo(char*) in the
original example is a different matter: which of the two overloads
should I use when passed a nullptr, which is an "untyped" null pointer
constant that potentially matches both of them?

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Wed, 22 Nov 2006 16:37:27 GMT
Raw View
Greg Herlihy ha scritto:
> Alberto Ganesh Barbati wrote:
>> Greg Herlihy ha scritto:
>>>> This approach would remove completely the need for the unspecified t=
ypes
>>>> T1 and T2.
>>> As long as T1 and T2 cannot be deduced from a nullptr argument by
>>> itself, then there is no harm in nullptr_t being both a pointer and a
>>> member pointer type.
>> No harm? I've shown you plenty of cases when harm is actually done. Ho=
w
>> can you match a pointer type while not deduce the pointed-to type? Eve=
n
>> if you amend the language to do it, how could that be useful?
>=20
> Unless I am very much mistaken, you have written at great length (and
> quite recently) the reasons why you think this precise behavior (of not
> having nullptr match a pointer or member pointer specialization on its
> own) is useful. In fact, I am not inclined to mediate the disagreement
> that you are now apparently having with yourself. In any event, if you
> still must disagree with me even after I have come around to your
> position, then I would have to agree that continuing this discussion is
> unlikely to be productive.

My statement above was not contradicting any other previous statements
of mine, or at least that wasn't my intention. If it gave you such
impression, I apologize. One fact is that I have misinterpreted your
statement. It wasn't apparent to me that you meant that nullptr_t should
*not* match a pointer type, but the opposite. That's what I was
objecting to.

With this new (hopefully correct) interpretation, I agree that it's no
harm to have nullptr_t being a pointer type, but I also see no point in
doing so. Could you please elaborate about what advantages we would gain
from that?

> On the other hand, if the three of us are able to agree on a common
> behavior (that nullptr should not match a pointer or member pointer
> specialization by itself), then we only have to decide how best to
> explain it. Here we have no choice but to amend the language in
> =A714.8.2.5 that currently states that T is deductible in a T* pointer
> specialization. Unless we make an nullptr an exception to this rule,
> any other change - such as your suggestion - leads to a contradiction.

Could you make me see what contradiction would my suggestion lead to, in
absence of the amendment you speak about? I can't see it by myself.

> But once we have an exception for nullptr in =A714.8.2.5, it becomes
> clear that we have finished the job. We do not have to change anything
> else - because we now have the behavior we deemed best, and we have an
> explanation for that this behavior that required only a minor change
> and not the wholesale rewrite of what is no doubt the most complex and
> dimly understood portion of the entire C++ Standard (and that is saying
> a lot). I am referring, of course, to the rules for template argument
> type deduction.=20

It seems to me that both your suggestions and mine are of about the same
"magnitude" and affect the very same part of the standard. So I don't
see this argument as supporting one suggestion against the other.

>From your statement above it seems that you think my suggestion won't be
enough without also including yours, but that yours would suffice.
However, until I see the contradiction you mentioned, I can't agree with
this position.

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: musiphil@bawi.org (Seungbeom Kim)
Date: Wed, 22 Nov 2006 16:39:51 GMT
Raw View
Joe Gottman wrote:
>>
>> foo(static_cast<int*>(0));
>> foo(static_cast<char*>(0));
>>
>> (By the way, this is one of the reasons why I'm sceptical about the
>> nullptr proposal; you cannot go without specifying the type completely.)
>
> Making users call foo(static_cast<int *>) is ugly.

Yes, but it's what it is, and attempts to hide the ugliness hasn't
seemed very satisfactory to me: there are cases when you have to specify
the type explicitly.

    void foo(int*);
    void foo(char*);
    foo(nullptr);  // won't work: call which one?

    template <typename T> void foo(T*);
    foo(nullptr);  // won't work: what is T?

Personally, I have found the plain 0 satisfactory for most cases. It was
only in very rare cases, such as vararg functions like the execl*()
family, that I had to resort to static_cast, in which case nullptr won't
work anyway because
1. static_cast<int*>(0) and static_cast<char*>(0) may have different
representations, and
2. the type cannot be deduced because of the ambiguity.

And if we cannot hide the type completely, there's not much to be gained
from adopting a new keyword instead of using the existing mechanism (0
or static_cast<T*>(0)).

Nullptr does help (and seems to be designed to help) in this case:

    void foo(int);
    void foo(int*);
    foo(0);         // calls foo(int)
    foo(nullptr);   // calls foo(int*);

but I don't remember having met such a case for the past many years. I
wonder whether other people encounter such cases a lot.

If static_cast<int*>(0) looks too ugly, I can imagine something along
the lines of nullptr<int*>() or nullptr<int>(), but I don't feel
anything much better than that is possible. I could be wrong; in which
case all of us are lucky. :)

--
Seungbeom Kim

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Peter Dimov" <pdimov@gmail.com>
Date: Wed, 22 Nov 2006 22:20:22 CST
Raw View
Seungbeom Kim wrote:

> Personally, I have found the plain 0 satisfactory for most cases. It was
> only in very rare cases, such as vararg functions like the execl*()
> family, that I had to resort to static_cast, in which case nullptr won't
> work anyway because
> 1. static_cast<int*>(0) and static_cast<char*>(0) may have different
> representations, and
> 2. the type cannot be deduced because of the ambiguity.

Repost:

std::vector< int* > v;

std::fill( v.begin(), v.end(), 0 ); // fails
std::remove( v.begin(), v.end(), 0 ); // fails

Here's one more:

void f( int* );

boost::bind( f, 0 )(); // fails

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: jgottman@carolina.rr.com ("Joe Gottman")
Date: Thu, 23 Nov 2006 19:03:05 GMT
Raw View
[Repost: the first one was lost]

"Seungbeom Kim" <musiphil@bawi.org> wrote in message
news:ek18gj$kda$1@news.Stanford.EDU...
> Joe Gottman wrote:
>>>
>>> foo(static_cast<int*>(0));
>>> foo(static_cast<char*>(0));
>>>
>>> (By the way, this is one of the reasons why I'm sceptical about the
>>> nullptr proposal; you cannot go without specifying the type completely.)
>>
>> Making users call foo(static_cast<int *>) is ugly.
>
> Yes, but it's what it is, and attempts to hide the ugliness hasn't
> seemed very satisfactory to me: there are cases when you have to specify
> the type explicitly.
>
>    void foo(int*);
>    void foo(char*);
>    foo(nullptr);  // won't work: call which one?
>
   You edited out the important fact that I defined an extra overload
    void foo(nullptr_t);

If we define this function, then foo(nullptr) calls foo(nullptr_t), no
matter what other functions or template functions are defined.

Joe Gottman


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: musiphil@bawi.org (Seungbeom Kim)
Date: Fri, 24 Nov 2006 18:37:38 GMT
Raw View
Joe Gottman wrote:
> [Repost: the first one was lost]
>
> "Seungbeom Kim" <musiphil@bawi.org> wrote in message
> news:ek18gj$kda$1@news.Stanford.EDU...
>> Joe Gottman wrote:
>>>> foo(static_cast<int*>(0));
>>>> foo(static_cast<char*>(0));
>>>>
>>>> (By the way, this is one of the reasons why I'm sceptical about the
>>>> nullptr proposal; you cannot go without specifying the type completely.)
>>> Making users call foo(static_cast<int *>) is ugly.
>> Yes, but it's what it is, and attempts to hide the ugliness hasn't
>> seemed very satisfactory to me: there are cases when you have to specify
>> the type explicitly.
>>
>>    void foo(int*);
>>    void foo(char*);
>>    foo(nullptr);  // won't work: call which one?
>>
>    You edited out the important fact that I defined an extra overload
>     void foo(nullptr_t);
>
> If we define this function, then foo(nullptr) calls foo(nullptr_t), no
> matter what other functions or template functions are defined.

Oh yes; I was talking only about the current status under which we have
no nullptr_t. Sorry if my post was misleading.

But I still don't see how one would define foo(nullptr_t) sensibly in
this case even when we have nullptr. If we have overloads of foo(T*) for
different T's (instead of a single template), it probably means those
functions have to do different things, and also that it is the client
that should have been more explicit in which one it wants. I have to
come up with a real-world example I cannot think of right now, though.

--
Seungbeom Kim

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Andrei Polushin" <polushin@gmail.com>
Date: Sat, 18 Nov 2006 12:40:25 CST
Raw View
Greg Herlihy wrote:
> Alberto Ganesh Barbati wrote:
>> Good point. Concepts... So we have one more problem. My point about
>> shared_ptr was: if nullptr_t matches T*, is T complete? Now we have a
>> bigger question: what concepts does T implement?
>
> Perform a concept check and find out. A nullptr_t is about as useful in
> generic programming as it is as a function parameter - that is, of no
> use at all.

There is no need for nullptr_t type at all. There is a need for nullptr
constant that could be a better replacement the integral constant zero.

Thus we can avoid the discussion about the possible meaning of
remove_pointer<nullptr_t>, because there is no need for nullptr_t.

Really, I don't see the need to obtain the type of the nullptr, because
we need it in a role of constant only. The constant should be

    - convertible to T1* when the pointer is expected;
    - convertible to T2*:: when the pointer to member is expected;
    - not convertible to int.

Thus nullptr is a constant similar to NULL, except for the last point.


Greg Herlihy wrote:
> Joe Gottman wrote:
>> Also, many functions that take a pointer as a parameter have
>> code that looks like the following:
>>
>> template <class T>
>> void usePointer(T *p)
>> {
>>     if (!p) {
>>         return; // or throw an exception
>>    } else {
>>        //Do something with *p
>>    }
>> }
>>
>> The problem is that the code under "Do something with *p" can do literally
>> anything.
>
> The code can try to do anything, but whether the routine is well-formed
> depends on a suitable selection for type T. Furthermore, even if this
> routine successfully copiles when p is a null pointer constant, the
> code after the comment will be completely unreachable and will never
> execute anyway.

I agree. The "nullness" is the runtime property of p, so passing the
nullptr doesn't neccessarily make the function statically incorrect.
In particular, the shared_ptr constructor needs to be statically
correct when called with nullptr parameter. Taking the sizeof(T1)
or instantiating T1() may be statically incorrect, I guess.


--
Andrei Polushin

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Peter Dimov" <pdimov@gmail.com>
Date: Sat, 18 Nov 2006 18:22:40 CST
Raw View
Andrei Polushin wrote:

> There is no need for nullptr_t type at all.

There is.

std::vector< int* > v;

std::fill( v.begin(), v.end(), nullptr );
std::remove( v.begin(), v.end(), nullptr );

If nullptr doesn't work in these and similar cases, it's not much an
improvement over NULL.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Sun, 19 Nov 2006 18:13:14 GMT
Raw View
Greg Herlihy ha scritto:
> Alberto Ganesh Barbati wrote:
>> Good point. Concepts... So we have one more problem. My point about
>> shared_ptr was: if nullptr_t matches T*, is T complete? Now we have a
>> bigger question: what concepts does T implement?
>
> Perform a concept check and find out. A nullptr_t is about as useful in
> generic programming as it is as a function parameter - that is, of no
> use at all.

Apparently you didn't get my point. Consider this:

  template <typename T>
  void foo(T*);

  template <CopyConstructible T>
  void bar(T*)

  foo(nullptr); // legal with T = unspecified type, but is that useful?
  bar(nullptr); // illegal? it depends on the unspecified type T1

As the paper does not specify which features T1 is expected to have, an
implementation might choose a T1 which is CopyConstructible, while
another implementation might not. So it would be implementation-defined
whether the second call is ill-formed or not. I find this unacceptable.

Anyway I come to the conclusion that nullptr should *not* match T* in
the first place (but see below). If nullptr_t was just a "POD-type
convertible to any pointer type" it would be so. The idea that nullptr_t
should match a pointer type template argument as-if it really was some
"pointer to unspecified type" is a source of endless problems.

>
> I think that default function template type parameters were probably
> invented for this very purpose:
>
>     template <class T = int *, class U = int *>
>     void accept_nullptr(  T t, U u );
>
> Then the calls to accept_nullptr() should work as shown above.

Interesting. I wasn't aware that the new draft already allows default
parameter for function templates (they are not allowed in C++03), but I
should have thought about that. However, your example is incorrect,
because accept_nullptr(nullptr, nullptr) should match T=nullptr_t and
U=nullptr_t, in my opinion. If it were written as:

  template <class T = int, class U = int>
  void accept_nullptr(T* t, U* u);

then nullptr might actually trigger substitution of default template
parameters and match as a pointer.

In view of this, I can correct my point of view stated above, which can
now be summarized as the following:

a) nullptr_t is POD-type convertible to any pointer type, in particular
nullptr_t is *not* a pointer type

b) if nullptr or any expression of type nullptr_t is used as the
argument of a function template that requires deduction:

  1) if the type is a T, it should match nullptr_t

  2) if the type is a cv T*, deduction is deferred. If T can be deduced
using another matching function argument, that T is used. Otherwise, if
a default parameter is present, it is used. Otherwise, there is no match

  3) similarly for pointers to members

This approach would remove completely the need for the unspecified types
T1 and T2.

Comments?

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Andrei Polushin" <polushin@gmail.com>
Date: Mon, 20 Nov 2006 00:21:28 CST
Raw View
Peter Dimov wrote:
> Andrei Polushin wrote:
>> There is no need for nullptr_t type at all.
>
> There is.
>
> std::vector< int* > v;
>
> std::fill( v.begin(), v.end(), nullptr );
> std::remove( v.begin(), v.end(), nullptr );
>
> If nullptr doesn't work in these and similar cases, it's not much an
> improvement over NULL.

Well, yes. It seems to be unresolvable this way.

Then I would prefer to further separate the usual pointer type from
the pointer to member type:

- nullptr is a constant of type T1* :

    std::vector<int*> v1;
    std::remove(v1.begin(), v1.end(), nullptr); // ok

- nullmemptr is another constant, convertible to T2::* :

    class A {};
    std::vector<int A::*> v2;
    v2.push_back(nullmemptr); // ok
    std::remove(v2.begin(), v2.end(), (int A::*)nullmemptr); // rare

    std::vector<int(A::*)()> v3;
    v3.push_back(nullmemptr); // ok
    std::remove(v3.begin(), v3.end(), (int(A::*)())nullmemptr);

Now there is still no need for nullptr_t type.


--
Andrei Polushin

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: jgottman@carolina.rr.com ("Joe Gottman")
Date: Tue, 21 Nov 2006 00:33:27 GMT
Raw View
"Andrei Polushin" <polushin@gmail.com> wrote in message
news:1163904121.562134.87810@h54g2000cwb.googlegroups.com...
> Peter Dimov wrote:
>> Andrei Polushin wrote:
>>> There is no need for nullptr_t type at all.
>>
>> There is.
>>
>> std::vector< int* > v;
>>
>> std::fill( v.begin(), v.end(), nullptr );
>> std::remove( v.begin(), v.end(), nullptr );
>>
>> If nullptr doesn't work in these and similar cases, it's not much an
>> improvement over NULL.
>
> Well, yes. It seems to be unresolvable this way.
>
> Then I would prefer to further separate the usual pointer type from
> the pointer to member type:
>
> - nullptr is a constant of type T1* :
>
>    std::vector<int*> v1;
>    std::remove(v1.begin(), v1.end(), nullptr); // ok
>
> - nullmemptr is another constant, convertible to T2::* :
>
>    class A {};
>    std::vector<int A::*> v2;
>    v2.push_back(nullmemptr); // ok
>    std::remove(v2.begin(), v2.end(), (int A::*)nullmemptr); // rare
>
>    std::vector<int(A::*)()> v3;
>    v3.push_back(nullmemptr); // ok
>    std::remove(v3.begin(), v3.end(), (int(A::*)())nullmemptr);
>
> Now there is still no need for nullptr_t type.

   How about the following:

   void foo(int *p);
   void foo(char *);

Which function should be called when the user calls foo(nullptr)?  Both
overloads are equally good matches.  The obvious solution is to add a third
overload:

void foo(nullptr_t);

Joe Gottman

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Tue, 21 Nov 2006 05:16:35 GMT
Raw View
Apparently, my previous post didn't stir much interest, but I feel it's
important for the discussion about nullptr, so I'll answer myself.

Alberto Ganesh Barbati ha scritto:
>
> In view of this, I can correct my point of view stated above, which can
> now be summarized as the following:
>
> a) nullptr_t is POD-type convertible to any pointer type, in particular
> nullptr_t is *not* a pointer type

Not only nullptr_t is not a pointer type, but it does not match a
pointer type in template argument list (neither "regular" pointers nor
pointers to member). This would also mean that the behavior of the
partial specialization example in the paper is different:

template<typename T> class X { };
template<typename T> class X<T*> { };
template<typename T> class X<T::*> { }; // attention: pseudo-code!

X<nullptr_t> x; // ok, matches main template with T=nullptr_t

My opinion is that nullptr/nullptr_t should receive a special treatment
only when used as an argument of a function template, as described in
point b) below. Such special treatment should be limited to function
template parameter deduction rules only.

> b) if nullptr or any expression of type nullptr_t is used as the
> argument of a function template that requires deduction:
>
>   1) if the type is a T, it should match nullptr_t
>
>   2) if the type is a cv T*, deduction is deferred. If T can be deduced
> using another matching function argument, that T is used. Otherwise, if
> a default parameter is present, it is used. Otherwise, there is no match
>
>   3) similarly for pointers to members

I realized that this rule can be stated differently in a more simple
way: if the argument type is cv T or cv T&, then T is deduced to be
nullptr_t, otherwise the argument does not contribute in any way in the
deduction process. For example:

template <typename T> void a(T);
template <typename T> void b(T*);
template <typename T = int> void c(T*);
template <typename T> void d(T x, T y);
template <typename T> void e(T* x, T* y);

a(nullptr);       // ok: a<nullptr_t>
a<int*>(nullptr); // ok: a<int*>
b(nullptr);       // error: can't deduce T
b<int>(nullptr);  // ok: b<int>
c(nullptr);       // ok: c<int>

int i
d(&i, nullptr);       // error: ambiguity, T can be int* or nullptr_t
d<int*>(&i, nullptr); // ok: b<int*>
e(&i, nullptr);       // ok: e<int>

Only the case of d is a bit less intuitive. That can be fixed too, if we
deem it necessary by applying a "twist" at the deduction algorithm:

  1) deduction is first attempted without considering any argument
evaluating to nullptr_t. If every template parameter can be assigned,
deduction succeeds,

  2) otherwise, for all parameters corresponding to nullptr_t arguments,
that are in the form cv T or cv T&, T is deduced to be nullptr_t. If
every template parameter can be assigned, deduction succeeds,

  3) otherwise deduction fails.

With this twisted rule, d(&i, nullptr) would be legal and would match
d<int*>.

Notice that with this rule, even if deduction succeeds, the code could
be ill-formed, for example:

  d(i, nullptr); // error: T is successfully deduced as int
                 // but there's no conversion from nullptr to int

(with the previous rule, T could have been both int and nullptr_t and
deduction would have failed).

Notice that both rules correctly handle Peter Dimov's examples:

std::vector< int* > v;
std::fill( v.begin(), v.end(), nullptr );
std::remove( v.begin(), v.end(), nullptr );

Just my 2 eurocent,

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: musiphil@bawi.org (Seungbeom Kim)
Date: Tue, 21 Nov 2006 06:11:17 GMT
Raw View
Joe Gottman wrote:
> "Andrei Polushin" <polushin@gmail.com> wrote in message
> news:1163904121.562134.87810@h54g2000cwb.googlegroups.com...
>>
>> Now there is still no need for nullptr_t type.
>
>    How about the following:
>
>    void foo(int *p);
>    void foo(char *);
>
> Which function should be called when the user calls foo(nullptr)?  Both
> overloads are equally good matches.  The obvious solution is to add a third
> overload:
>
> void foo(nullptr_t);

What should the new function do? It's the caller that should have been
clearer and unambiguous by stating which one it wanted, isn't it?

foo(static_cast<int*>(0));
foo(static_cast<char*>(0));

(By the way, this is one of the reasons why I'm sceptical about the
nullptr proposal; you cannot go without specifying the type completely.)

--
Seungbeom Kim

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Tue, 21 Nov 2006 00:11:54 CST
Raw View
Alberto Ganesh Barbati wrote:
> In view of this, I can correct my point of view stated above, which can
> now be summarized as the following:
>
> a) nullptr_t is POD-type convertible to any pointer type, in particular
> nullptr_t is *not* a pointer type
>
> b) if nullptr or any expression of type nullptr_t is used as the
> argument of a function template that requires deduction:
>
>   1) if the type is a T, it should match nullptr_t
>
>   2) if the type is a cv T*, deduction is deferred. If T can be deduced
> using another matching function argument, that T is used. Otherwise, if
> a default parameter is present, it is used. Otherwise, there is no match
>
>   3) similarly for pointers to members
>
> This approach would remove completely the need for the unspecified types
> T1 and T2.

As long as T1 and T2 cannot be deduced from a nullptr argument by
itself, then there is no harm in nullptr_t being both a pointer and a
member pointer type.

In order for the default template parameters to provide pointer type
information when a T* specialization is called with a nullptr argument,
nullptr_t first has to match the T* specialization (thereby requiring
that nullptr_t be - and not just be convertible to - a pointer type).

But simply because nullptr matches T* - does not mean that T's type can
also be deduced. So I would suggest adding some language to    14.8.2.5
that states that although nullptr_t matches pointer and member pointer
specializations, the types for those specializations cannot be deduced.

Greg


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Tue, 21 Nov 2006 16:50:23 GMT
Raw View
Greg Herlihy ha scritto:
>>
>> This approach would remove completely the need for the unspecified typ=
es
>> T1 and T2.
>=20
> As long as T1 and T2 cannot be deduced from a nullptr argument by
> itself, then there is no harm in nullptr_t being both a pointer and a
> member pointer type.

No harm? I've shown you plenty of cases when harm is actually done. How
can you match a pointer type while not deduce the pointed-to type? Even
if you amend the language to do it, how could that be useful?

I did not see in this thread convincing answers to my objections (in
particular tr1::remove_pointer, foo(nullptr) implementation-defined when
using concepts, T1 being a complete type, and so on). All I get is just
snipping them and avoiding to answer, while objecting that the T1/T2
approach is "obviously" the right way. It's far from obvious to me.

> In order for the default template parameters to provide pointer type
> information when a T* specialization is called with a nullptr argument,
> nullptr_t first has to match the T* specialization (thereby requiring
> that nullptr_t be - and not just be convertible to - a pointer type).
> But simply because nullptr matches T* - does not mean that T's type can
> also be deduced.

I posted a follow-up to my own post with a revised deduction algorithm.
I removed the need to match a T* in order to trigger the use of default
arguments. None of these objections hold with the new algorithm.

> also be deduced. So I would suggest adding some language to =A714.8.2.5
> that states that although nullptr_t matches pointer and member pointer
> specializations, the types for those specializations cannot be deduced.

As I said before, I find this idea both useless and harmful.

BTW: Does anyone know what's the actual state of the nullptr proposal?
According to paper N2122 there should be a paper with complete wording.
However, the most recent reference is N1601 which is two years old, it
does not have wording and it's severely underspecified, IMHO. If there's
 wording available, could it be made public, please? This discussion
doesn't make any sense if things are already settled down, but we don't
know in which way...

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: jgottman@carolina.rr.com ("Joe Gottman")
Date: Wed, 22 Nov 2006 05:43:05 GMT
Raw View
[REPOST:  My original post seems to be lost]

"Seungbeom Kim" <musiphil@bawi.org> wrote in message
news:ejtnr9$2q5$1@news.Stanford.EDU...
> Joe Gottman wrote:
>> "Andrei Polushin" <polushin@gmail.com> wrote in message
>> news:1163904121.562134.87810@h54g2000cwb.googlegroups.com...
>>>
>>> Now there is still no need for nullptr_t type.
>>
>>    How about the following:
>>
>>    void foo(int *p);
>>    void foo(char *);
>>
>> Which function should be called when the user calls foo(nullptr)?  Both
>> overloads are equally good matches.  The obvious solution is to add a
>> third
>> overload:
>>
>> void foo(nullptr_t);
>
> What should the new function do? It's the caller that should have been
> clearer and unambiguous by stating which one it wanted, isn't it?
>
> foo(static_cast<int*>(0));
> foo(static_cast<char*>(0));
>
> (By the way, this is one of the reasons why I'm sceptical about the
> nullptr proposal; you cannot go without specifying the type completely.)
>


Making users call foo(static_cast<int *>) is ugly.  Usually the library will
do the same thing with any null pointer.  Often this will be something
simple, for instance returning immediately, throwing an exception, or
default constructing an object. After all, a null pointer has no state so
there can't be much decision-making logic. Even when the code is  more
complicated, the other two functions can be coded in terms of foo(nullptr):

void foo(int *p)
{
    if (!p) {
        foo(nullptr);
   }
   // Continue with int * specific logic
}

and similar for foo(char *p).


Joe Gottman



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Tue, 21 Nov 2006 23:40:31 CST
Raw View
Alberto Ganesh Barbati wrote:
> Greg Herlihy ha scritto:
> >>
> >> This approach would remove completely the need for the unspecified types
> >> T1 and T2.
> >
> > As long as T1 and T2 cannot be deduced from a nullptr argument by
> > itself, then there is no harm in nullptr_t being both a pointer and a
> > member pointer type.
>
> No harm? I've shown you plenty of cases when harm is actually done. How
> can you match a pointer type while not deduce the pointed-to type? Even
> if you amend the language to do it, how could that be useful?

Unless I am very much mistaken, you have written at great length (and
quite recently) the reasons why you think this precise behavior (of not
having nullptr match a pointer or member pointer specialization on its
own) is useful. In fact, I am not inclined to mediate the disagreement
that you are now apparently having with yourself. In any event, if you
still must disagree with me even after I have come around to your
position, then I would have to agree that continuing this discussion is
unlikely to be productive.

On the other hand, if the three of us are able to agree on a common
behavior (that nullptr should not match a pointer or member pointer
specialization by itself), then we only have to decide how best to
explain it. Here we have no choice but to amend the language in
   14.8.2.5 that currently states that T is deductible in a T* pointer
specialization. Unless we make an nullptr an exception to this rule,
any other change - such as your suggestion - leads to a contradiction.

But once we have an exception for nullptr in    14.8.2.5, it becomes
clear that we have finished the job. We do not have to change anything
else - because we now have the behavior we deemed best, and we have an
explanation for that this behavior that required only a minor change
and not the wholesale rewrite of what is no doubt the most complex and
dimly understood portion of the entire C++ Standard (and that is saying
a lot). I am referring, of course, to the rules for template argument
type deduction.

Greg


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Thu, 16 Nov 2006 22:17:17 GMT
Raw View
[note to Mods: this is a repost]

Andrei Polushin ha scritto:
> > In context of N1601, the nullptr proposal,
> > Andrei Polushin wrote:
>> >> if nullptr_t is a combination of both types, it is defined in terms
>> >> of both declarations:
>> >>
>> >>         T1 * D1
>> >>         T2 :: * D2
> >
> > Alberto Ganesh Barbati wrote:
>> >> I believe the paper is defective because while it explicitly says that
>> >> nullptr_t matches both T* and T::*, but doesn't specify for which T!
> >
> > I believe it cannot specify which T, because it specifies that T is
> > both T1 and T2. Both types have the opposite characteristics.

I know that T1 and T2 have opposite characteristics, but as nullptr
can't match both at the same time, it is reasonable to expect the paper
to at least specify T1 and T2 separately. This lack of
specification is a defect, IMHO. For example, the paper could say that
T1 and T2 are implementation-defined types satisfying a certain list of
properties. Even that might be enough.

>> >> Is T nullptr_t itself? It can't be, because nullptr_t* is
>> >> a valid type.
> >
> > It's valid in N1601, but there was no reason to allow it.
> >

Nor I see a reason to disallow it.

> >
>> >> IMHO, the only reasonable solution is that nullptr_t should *not*
match
>> >> the specialization but the main definition must be chosen with T =
>> >> nullptr_t.
> >
> > The questions:
> >
> >     1. Is it pointer? If it is pointer,
> >        why doesn't it match the specialization for <T*>?

My understanding was that nullptr wasn't actually a pointer, but simply
"convertible" to any pointer type. It seems I was wrong.

> >     2. Is it useful in generic programming?
> >
> > The problem with shared_ptr is the problem that will arise each time
> > when we will have a template function with parameter of pointer or
> > pointer-to-member type:
> >
> >     void f(char*);
> >     template<class T> void g(T* p) { f(p); }
> >
> >     g(NULL);    // allowed
> >     g(nullptr); // disallowed?

Not quite... g(NULL) is actually disallowed, because the compiler can't
deduce T. So this example simply shows that if nullptr doesn't match T*
then we are stuck with the ugly idiom

  g((char*)NULL);

which nullptr was expected to fix.

As we are discussing about the type of nullptr, what would be the
expected behavior of

   tr1::is_pointer<nullptr_t>
   tr1::is_member_object_pointer<nullptr_t>
   tr1::is_member_function_pointer<nullptr_t>

and

   tr1::remove_pointer<nullptr_t> ?

In particular, the latter seems a bit problematic, yet very interesting.

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Fri, 17 Nov 2006 03:08:04 GMT
Raw View
Andrei Polushin ha scritto:
> In context of N1601, the nullptr proposal,
> Andrei Polushin wrote:
>> if nullptr_t is a combination of both types, it is defined in terms
>> of both declarations:
>>
>>         T1 * D1
>>         T2 :: * D2
>
> Alberto Ganesh Barbati wrote:
>> I believe the paper is defective because while it explicitly says that
>> nullptr_t matches both T* and T::*, but doesn't specify for which T!
>
> I believe it cannot specify which T, because it specifies that T is
> both T1 and T2. Both types have the opposite characteristics.

I know that T1 and T2 have opposite characteristics, but as nullptr
can't match both at the same time, it is reasonable to expect the paper
to at least specify T1 and T2 separately. This lack of
specification is a defect, IMHO. For example, the paper could say that
T1 and T2 are implementation-defined types satisfying a certain list of
properties. Even that might be enough.

>> Is T nullptr_t itself? It can't be, because nullptr_t* is
>> a valid type.
>
> It's valid in N1601, but there was no reason to allow it.
>

Nor I see a reason to disallow it.

>
>> IMHO, the only reasonable solution is that nullptr_t should *not* match
>> the specialization but the main definition must be chosen with T =
>> nullptr_t.
>
> The questions:
>
>     1. Is it pointer? If it is pointer,
>        why doesn't it match the specialization for <T*>?

My understanding was that nullptr wasn't actually a pointer, but simply
"convertible" to any pointer type. It seems I was wrong.

>     2. Is it useful in generic programming?
>
> The problem with shared_ptr is the problem that will arise each time
> when we will have a template function with parameter of pointer or
> pointer-to-member type:
>
>     void f(char*);
>     template<class T> void g(T* p) { f(p); }
>
>     g(NULL);    // allowed
>     g(nullptr); // disallowed?

Not quite... g(NULL) is actually disallowed, because the compiler can't
deduce T. So this example simply shows that if nullptr doesn't match T*
then we are stuck with the ugly idiom

  g((char*)NULL);

which nullptr was expected to fix.

As we are discussing about the type of nullptr, what would be the
expected behavior of

   tr1::is_pointer<nullptr_t>
   tr1::is_member_object_pointer<nullptr_t>
   tr1::is_member_function_pointer<nullptr_t>

and

   tr1::remove_pointer<nullptr_t> ?

In particular, the latter seems a bit problematic, yet very interesting.

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Joe Gottman" <jgottman@carolina.rr.com>
Date: Thu, 16 Nov 2006 23:48:02 CST
Raw View
"Alberto Ganesh Barbati" <AlbertoBarbati@libero.it> wrote in message
news:ZJF6h.43747$uv5.300164@twister1.libero.it...
> Andrei Polushin ha scritto:
>> In context of N1601, the nullptr proposal,
>> Andrei Polushin wrote:
>>> if nullptr_t is a combination of both types, it is defined in terms
>>> of both declarations:
>>>
>>>         T1 * D1
>>>         T2 :: * D2
>>
>> Alberto Ganesh Barbati wrote:
>>> I believe the paper is defective because while it explicitly says that
>>> nullptr_t matches both T* and T::*, but doesn't specify for which T!
>>
>> I believe it cannot specify which T, because it specifies that T is
>> both T1 and T2. Both types have the opposite characteristics.

   What is the advantage of this over just having nullptr_t be a simple type
with defined conversions to all pointer and member-pointer types?  As the
previous poster noted, having nullptr_t be of both pointer and
member-pointer type breaks the perfectly reasonable assumption in the
type_traits library that the sets of pointer types and member-pointer types
are disjoint.  Also, many functions that take a pointer as a parameter have
code that looks like the following:

template <class T>
void usePointer(T *p)
{
    if (!p) {
        return; // or throw an exception
   } else {
       //Do something with *p
   }
}

The problem is that the code under "Do something with *p" can do literally
anything.  It can call any function with *p as a parameter and any number of
other parameters of any type before and after.  It can call a member
function on p of any name with any number of parameters.  And yet somehow,
in all cases, this code must compile with T = T1.  If the concepts proposal
is accepted then this becomes even weirder.

It seems to me that it would be much simpler for all involved, compile
writers, library writers, and casual users, if nullptr_t were just a simple
type with an automatic conversion to any pointer or member-pointer type.
Then the rule for using it would be easy to understand: if your overload set
has one function that takes a pointer parameter then nullptr converts to
that type.  If it has two or more or is a template than you need to write an
extra overload that takes a nullptr_t parameter.

Joe Gottman


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Fri, 17 Nov 2006 16:40:37 GMT
Raw View
Joe Gottman ha scritto:
>>> Alberto Ganesh Barbati wrote:
>>>> I believe the paper is defective because while it explicitly says that
>>>> nullptr_t matches both T* and T::*, but doesn't specify for which T!
>>> I believe it cannot specify which T, because it specifies that T is
>>> both T1 and T2. Both types have the opposite characteristics.
>

The more I think of it, the more I realize that letting nullptr_t match
T::* is a nonsense. The first objection is that there is no such thing
in C++ as T::*!!! Consider a template that takes a member pointer
parameter, how do you write it? Surely not like this:

  template<typename T> class X { };
  template<typename T> class X<T::*> { }; // case X<T::*>

as is described in the paper! For a pointer to data member, you would
have to write:

  template<typename T> class X<MemberTypeHere T::*> { };

but also this is perfectly legal:

  template<typename T, typename V> class X<V T::*> { };

It has been said in this thread that when nullptr_t matches V T::* then
T is assigned some implementation defined type that do to trick (called
by Andrei most_base_t). What about V?

For a pointer to member function it's even more complex, because you
have to specify the function signature.

>    What is the advantage of this over just having nullptr_t be a simple type
> with defined conversions to all pointer and member-pointer types?  As the
> previous poster noted, having nullptr_t be of both pointer and
> member-pointer type breaks the perfectly reasonable assumption in the
> type_traits library that the sets of pointer types and member-pointer types
> are disjoint.

I like the idea that pointer types and pointer-to-member types shall be
disjoint set. In view of this and the above argument, the idea of
letting nullptr_t match a pointer-to-member type, while appealing, seems
to be creating more problems than it solves.

> Also, many functions that take a pointer as a parameter have
> code that looks like the following:
>
> template <class T>
> void usePointer(T *p)
> {
>     if (!p) {
>         return; // or throw an exception
>    } else {
>        //Do something with *p
>    }
> }
>
> The problem is that the code under "Do something with *p" can do literally
> anything.  It can call any function with *p as a parameter and any number of
> other parameters of any type before and after.  It can call a member
> function on p of any name with any number of parameters.  And yet somehow,
> in all cases, this code must compile with T = T1.  If the concepts proposal
> is accepted then this becomes even weirder.

Good point. Concepts... So we have one more problem. My point about
shared_ptr was: if nullptr_t matches T*, is T complete? Now we have a
bigger question: what concepts does T implement?

Maybe the only way to get out of the loop is to find a syntax that
explictly allows the programmer to select which T to match when
nullptr_t is used. Of course that can be obtained by overloading:

  template <class T>
  void accept_nullptr(T* ptr);

  inline void accept_nullptr(nullptr_t) { accept_nullptr((int*)0); }

but that's ugly. As nullptr is a new keyword, we can use it to create
more creative syntaxes, for example:

  template <class T>
  void accept_nullptr(T* ptr) nullptr(int*);

  accept_nullptr(nullptr); // matches int*

In case of multiple arguments:

  template <class T, class U>
  void accept_nullptr(T*, U*) nullptr(int*, int*);

  float f;
  accept_nullptr(&f, &f); // matches float*, float*
  accept_nullptr(nullptr, &f); // matches int*, float*
  accept_nullptr(&f, nullptr); // matches float*, int*
  accept_nullptr(nullptr, nullptr); // matches int*, int*

you don't need to disambiguate all arguments:

  template <class T, class U>
  void accept_nullptr(T*, U*) nullptr(int*,);

  template <class T, class U>
  void accept_nullptr(T*, U*) nullptr(, float*);

The arguments are matched positionally. Omitted arguments are not
disambiguated, so nullptr won't match in that case:

  template <class T, class U>
  void accept_nullptr(T*, U*) nullptr(int*,);

  float f;
  accept_nullptr(&f, &f); // matches float*, float*
  accept_nullptr(nullptr, &f); // matches int*, float*
  accept_nullptr(&f, nullptr); // no match!
  accept_nullptr(nullptr, nullptr); // no match!

It would be interesting to also allow this case:

  template <class T, class U>
  void accept_nullptr(T*, U*) nullptr(int*, T*);

  float f;
  accept_nullptr(nullptr, &f); // matches int*, float*
  accept_nullptr(&f, nullptr); // matches float*, float*
  accept_nullptr(nullptr, nullptr); // matches int*, int*

Non-pointer and non-dependent parameters can't be disambiguated:

  template <class T>
  void bad_example(T) nullptr(int*); // ill-formed

  template <class T>
  void bad_example(T*, int*) nullptr(, int*); // ill-formed

Of course the disambiguator works for pointer-to-members too:

  template <class T, class C>
  void memptr_example(T C::*) nullptr(int MyClass::*);

a parameter that can be deduced can be used in a disambiguator:

  template <class T, class C>
  void memptr_example(C&, T C::*) nullptr(int C::*);

  MyClass c;
  memptr_example(c, nullptr); // matches MyClass&, int MyClass::*

That would be more than enough for function templates. What about class
templates? Let's answer this question first: do we really need this
feature for class templates? I don't think so. Anyway, a syntax could be
the following:

  template <class T>
  class MyTemplate;

  template <class T>
  class MyTemplate<T*> nullptr(int*); // or nullptr<int*> ?

Any ideas?

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Fri, 17 Nov 2006 11:58:13 CST
Raw View
Joe Gottman wrote:
> "Alberto Ganesh Barbati" <AlbertoBarbati@libero.it> wrote in message
> news:ZJF6h.43747$uv5.300164@twister1.libero.it...
> > Andrei Polushin ha scritto:
> >> In context of N1601, the nullptr proposal,
> >> Andrei Polushin wrote:
> >>> if nullptr_t is a combination of both types, it is defined in terms
> >>> of both declarations:
> >>>
> >>>         T1 * D1
> >>>         T2 :: * D2
> >>
> >> Alberto Ganesh Barbati wrote:
> >>> I believe the paper is defective because while it explicitly says that
> >>> nullptr_t matches both T* and T::*, but doesn't specify for which T!
> >>
> >> I believe it cannot specify which T, because it specifies that T is
> >> both T1 and T2. Both types have the opposite characteristics.
>
>    What is the advantage of this over just having nullptr_t be a simple type
> with defined conversions to all pointer and member-pointer types?

Well, nullptr_t is a POD-type and, yes, it does convert implicitly to
any pointer or to any member pointer type.

> As the
> previous poster noted, having nullptr_t be of both pointer and
> member-pointer type breaks the perfectly reasonable assumption in the
> type_traits library that the sets of pointer types and member-pointer types
> are disjoint.

The sets are disjoint - except of course for the null pointer constant.
A null pointer constant can be compared and assigned to a pointer and
to a member pointer. Therefore we can conclude that a null pointer
constant must a hybrid type. In other words, nullptr doesn't change
anything in this regard.

> Also, many functions that take a pointer as a parameter have
> code that looks like the following:
>
> template <class T>
> void usePointer(T *p)
> {
>     if (!p) {
>         return; // or throw an exception
>    } else {
>        //Do something with *p
>    }
> }
>
> The problem is that the code under "Do something with *p" can do literally
> anything.

The code can try to do anything, but whether the routine is well-formed
depends on a suitable selection for type T. Furthermore, even if this
routine successfully copiles when p is a null pointer constant, the
code after the comment will be completely unreachable and will never
execute anyway.

> It can call any function with *p as a parameter and any number of
> other parameters of any type before and after.  It can call a member
> function on p of any name with any number of parameters.

Even when T is an "int" or a "void" type?

> And yet somehow,
> in all cases, this code must compile with T = T1.

What type is "T1" exactly?  And why "must" this template function
compile when passed a pointer to that type? As currently written, we
already know that this routine will fail to compile when p is a
nullptr. There is no nullptr-to-bool implicit conversion, so the "!p"
expression is invalid.

> It seems to me that it would be much simpler for all involved, compile
> writers, library writers, and casual users, if nullptr_t were just a simple
> type with an automatic conversion to any pointer or member-pointer type.
> Then the rule for using it would be easy to understand: if your overload set
> has one function that takes a pointer parameter then nullptr converts to
> that type. If it has two or more or is a template than you need to write an
> extra overload that takes a nullptr_t parameter.

The proposed nullptr would work exactly as you describe. What makes you
think otherwise?

Greg

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Sat, 18 Nov 2006 01:33:13 CST
Raw View
Alberto Ganesh Barbati wrote:
> Joe Gottman ha scritto:
> >>> Alberto Ganesh Barbati wrote:
> >>>> I believe the paper is defective because while it explicitly says that
> >>>> nullptr_t matches both T* and T::*, but doesn't specify for which T!
> >>> I believe it cannot specify which T, because it specifies that T is
> >>> both T1 and T2. Both types have the opposite characteristics.
> >
>
> The more I think of it, the more I realize that letting nullptr_t match
> T::* is a nonsense. The first objection is that there is no such thing
> in C++ as T::*!!!

The proposal is using the T::* syntax as a notational shorthand for all
member pointer types - it is not meant to be taken as a literal
declaration - because it's not.
.
> It has been said in this thread that when nullptr_t matches V T::* then
> T is assigned some implementation defined type that do to trick (called
> by Andrei most_base_t). What about V?

V T::* is a single, implementation-defined member pointer type. So the
class of the member pointer and the type of the data member or the
return type of the class method - are all, unsurprisingly,
implementation-defined.

> >    What is the advantage of this over just having nullptr_t be a simple type
> > with defined conversions to all pointer and member-pointer types?  As the
> > previous poster noted, having nullptr_t be of both pointer and
> > member-pointer type breaks the perfectly reasonable assumption in the
> > type_traits library that the sets of pointer types and member-pointer types
> > are disjoint.
>
> I like the idea that pointer types and pointer-to-member types shall be
> disjoint set. In view of this and the above argument, the idea of
> letting nullptr_t match a pointer-to-member type, while appealing, seems
> to be creating more problems than it solves.

Member pointers and pointers are not merely "disjoint", they are
completely different kinds of types. A pointer holds an address of an
object in memory - while a member pointer stores no address and points
to no object - a member pointer, despite its name, is not a pointer
type. So a template specialization for a pointer will never match a
member pointer type parameter, nor will a specialization for a member
pointer ever match a pointer type parameter. Both specializations will
match a nullptr_t type parameter, because a nullptr, like the null
pointer constant, is both types at once.

> > Also, many functions that take a pointer as a parameter have
> > code that looks like the following:
> >
> > template <class T>
> > void usePointer(T *p)
> > {
> >     if (!p) {
> >         return; // or throw an exception
> >    } else {
> >        //Do something with *p
> >    }
> > }
> >
> > The problem is that the code under "Do something with *p" can do literally
> > anything.  It can call any function with *p as a parameter and any number of
> > other parameters of any type before and after.  It can call a member
> > function on p of any name with any number of parameters.  And yet somehow,
> > in all cases, this code must compile with T = T1.  If the concepts proposal
> > is accepted then this becomes even weirder.
>
> Good point. Concepts... So we have one more problem. My point about
> shared_ptr was: if nullptr_t matches T*, is T complete? Now we have a
> bigger question: what concepts does T implement?

Perform a concept check and find out. A nullptr_t is about as useful in
generic programming as it is as a function parameter - that is, of no
use at all.

> Maybe the only way to get out of the loop is to find a syntax that
> explictly allows the programmer to select which T to match when
> nullptr_t is used. Of course that can be obtained by overloading:
>
>   template <class T>
>   void accept_nullptr(T* ptr);
>
>   inline void accept_nullptr(nullptr_t) { accept_nullptr((int*)0); }
>
> but that's ugly. As nullptr is a new keyword, we can use it to create
> more creative syntaxes, for example:
> In case of multiple arguments:
>
>   template <class T, class U>
>   void accept_nullptr(T*, U*) nullptr(int*, int*);
>
>   float f;
>   accept_nullptr(&f, &f); // matches float*, float*
>   accept_nullptr(nullptr, &f); // matches int*, float*
>   accept_nullptr(&f, nullptr); // matches float*, int*
>   accept_nullptr(nullptr, nullptr); // matches int*, int*

I think that default function template type parameters were probably
invented for this very purpose:

    template <class T = int *, class U = int *>
    void accept_nullptr(  T t, U u );

Then the calls to accept_nullptr() should work as shown above.

Greg

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]