
On Jan 27, 2004, at 11:06 AM, Peter Dimov wrote:
Need to be? No. But I still like it. <shrug> I was convinced because that's how a newbie expected it to work.
I'm starting to not like it. :-) The problem is:
template<class X> void f(S_ptr<X> px);
where f doesn't support arrays. A reasonably common occurence. S_array<> doesn't match, but S_ptr<T[]> does. The author of f would need to learn about enable_if to prevent array pointers from being passed to f. This is not good, although the exact degree of not-goodness is somewhat debatable.
I understand your hesitation. But I'm not yet seeing a concrete case to support it. There are two situations to analyze here: 1. Client writes: template<class X> void f(S_ptr<X> px); and intends to catch only pointers to single objects, and does not want to deal with pointers to arrays. 2. Client writes: template<class X> void f(S_ptr<X> px); and intends to catch pointers to both single objects and array objects. In case 1, learning about enable_if is not necessarily a bad thing. Indeed, a significant portion of the standard headers would be much better behaved today if we (as a community) had better understood the value of restricted templates, how to implement them, and the dangers of templates with unrestricted syntax, but restricted semantics. For example: template <class InputIterator, class Distance> void advance(InputIterator& i, Distance n); That is what we meant. But to the compiler we wrote: template <class T, class U> void advance(T& x, U y); The difference is really startling. Learning about enable_if is a feature, not a bug! :-) Now if the author intends case 1, enable_if is only one route to take. Another very easy thing to say is: template<class X> void f(S_ptr<X> px) { static_assert(!is_array<X>::value, "X must not be an array type"); ... } Reference: http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1424.htm http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1381.htm This boils down to: Is S_ptr<X[]> going to become a familiar enough idiom that the average C++ programmer is going to be educated enough to deal with it? That's a real question. Imho we either fully embrace it or fully abandon it. In case 2, where the programmer wants to collect smart pointers to single objects and arrays, then the syntax is clearly a benefit (as Bronek pointed out earlier in this thread). There isn't a whole lot that a client could do with both S_ptr<X> and S_ptr<X[]>, but that cuts both ways: If the attempted syntax is common, it will likely work: template<class X> void f(S_ptr<X> px) { px.reset(); } If the attempted syntax is not common, then it will likely not work: template<class X> void f(S_ptr<X> px) { *px; } Are there use cases where a programmer could naively assume 1, but end up with 2? Are there use cases where you really need 2? Looking for good use cases.... I've been exploring copy_ptr and clone_ptr lately. There is a connection that may be relevant. Consider vector<S_ptr<T> >. When you copy that container, what should be the semantics? vector<shared_ptr<T> > : copy shares ownership with source vector<move_ptr<T> > : copy not possible, but can move (transfer) ownership from source to target vector<copy_ptr<T> > : copy uses new T(*t) to "deep copy" each element. vector<clone_ptr<T> > : copy uses t->clone() to "deep copy" each element. In the first two cases, if T turns out to be an array type, there isn't that much to be concerned about. But in the latter two cases if T is an array type, you really need to know how many elements are in the array to pull off a correct copy. So... vector<copy_ptr<int[3]> > : copy_ptr's copy ctor could know how many elements are in the array and do the right thing (and similarly for clone_ptr<T[N]>). copy_ptr<T[N]> would only be convertible to copy_ptr<T[M]> if N == M. copy_ptr<T[]> would not be copyable at all because it doesn't know how many elements to copy. I'm just kind of rambling in public (which is always a foolish thing to do). I haven't prototyped any of this yet. Thoughts? -Howard