$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
From: Howard Hinnant (hinnant_at_[hidden])
Date: 2002-03-11 13:07:57
On Monday, March 11, 2002, at 12:22  PM, Rainer Deyke wrote:
> ----- Original Message -----
> From: "Howard Hinnant" <hinnant_at_[hidden]>
> To: <boost_at_[hidden]>
> Sent: Monday, March 11, 2002 7:12 AM
> Subject: Re: [boost] Re: container design (Was: std::complex design
> (Was: N-Dimensional array))
>
>
>>> The way I approach these things is by being explicit:
>>>
>>> template<class F> vector::vector(size_type n, init_tag, F f);
>>
>> If that's not an iron clad argument in favor of restricted templates
>> then I don't know what is. ;-)
>
> Restricted templates won't necessarily do the right thing.
>
> class initializer {
> public:
>   template<class T> void operator()(T *, size_t);
> };
>
> std::vector initializers(100, initializer());
Quite true.  But in this case, neither will manual implementation of the 
"do-the-right-thing" clause.  At least with restricted templates, the 
requirements on the template argument become part of the method's 
interface, instead of buried in the code (or buried in the 
documentation ... standard in this case).
We already have the problem today with the iterator template members.  
Consider:
struct BigNum
{
     operator std::size_t() const;
};
std::vector<BigNum> v(BigNum(), BigNum());
I'll bet this won't compile on your platform today (it won't compile on 
mine either).  And actually, according to the "do-the-right-thing" 
clause in the standard, it doesn't have to because BigNum is not an 
integral type.  But there's nothing in vector's interface that says that 
the above example should not compile while this example should:
std::vector<int> v(10, 0);
Both of these constructors bind to:
template <class InputIterator>
vector(InputIterator f, InputIterator l, const Allocator& a = 
Allocator());
But under the covers (somehow) the implementor must make v(10, 0) behave 
as if it bound to:
vector(size_type n, const value_type& x);
It would be so much more understandable, readable and predictable if the 
signature for the member template constructor could have been written:
template <class InputIterator : 
!numeric_limits<InputIterator>::is_integer>
vector(InputIterator f, InputIterator l, const Allocator& a = 
Allocator());
Then we can read right out of vector's interface, ok InputIterator can't 
be an integer according to numeric_limits.  The effect is the same as 
23.1.1/9, just more clearly defined.
Now that we have is_convertible, a better constraint might be to make 
sure that InputIterator can not convert both to a size_type and to a 
value_type.  That would allow the BigNum example above to work as well.  
With restricted templates, it is quite straight forward to change the 
constraint, now that we know of a better one.  Without restricted 
templates, changing the template constraint is much less straight 
forward.  So much so that seasoned C++ programmers either aren't sure 
how to do it, and/or resort to augmenting the function signature with 
tags to disambiguate the situation.
Assuming that part of the standard library's role is to display design 
worthy of emulation by Joe Programmer (the STL has certainly done 
this!), it seems clear to me that this is not just a problem in 
implementing the std::lib.  Dealing with this problem is something any 
programmer might face.  I think we can give the programmer better tools 
for this problem with minimal effort and no backwards compatibility hit.
-Howard