Boost logo

Boost :

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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk