$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
Subject: Re: [boost] [guidelines] why template errors suck
From: Doug Gregor (doug.gregor_at_[hidden])
Date: 2010-09-28 17:26:38
On Tue, Sep 28, 2010 at 1:04 PM, Sebastian Redl
<sebastian.redl_at_[hidden]> wrote:
>
> On Sep 28, 2010, at 12:18 PM, John Bytheway wrote:
>
>> For concreteness, I imagine that a "straightforward" implementation of
>> find_if in concept land looks something like this (like everyone else
>> here I reserve the right to get the syntax wrong, in my case having
>> never worked with concepts this way before):
>>
>> template<InputIterator I, typename F>
>> I find_if(I start, I const finish, F const p)
>> where Predicate<F, InputIterator<I>::value_type>
>> {
>> while (start != finish && !p(*start)) ++start;
>> return start;
>> }
>>
>> Thus, I imagine that the pseudo-signature technique allows this
>> implementation.
>>
>> Furthermore, I would have written the Predicate concept like
>>
>> auto concept Predicate<typename F, typename A>
>> {
>> bool operator()(A);
>> }
>
> Small syntax correction: You need F as the first parameter of that operator ().
>
>>
>> So, my first question is: will the above find_if compile?
>
> Yes.
>
>> If so, does
>> it imply that predicates must return bool, or merely that they must
>> return something convertible to bool?
>
> The latter. That's why they're called pseudo-signatures.
>
>> If the latter, then I don't see
>> any difference from the valid expression approach.
>
> The difference is that, no matter what the actual predicate returns, it is treated as a bool.
>
>> So, I guess it is
>> the former? In that case I think I see what you're driving at. It
>> prevents me from writing a perverse predicate such as:
>>
>> struct funny_bool {
>> operator bool() { return true; }
>> bool operator!() { return true; }
>> };
>>
>> struct predicate {
>> funny_bool operator()(int) { return funny_bool; }
>> };
>>
>> although that would have been fine under the valid-expressions framework
>> (and have potentially surprising behaviour).
>
> No, if I remember correctly how concepts work, that's actually a perfectly valid thing to do. In find_if<InputIterator, predicate>, p(*start) is bound to the pseudo-signature operator(), which returns a bool. Therefore, the result of p(*start) is converted to a bool first, and only then is operator ! applied to it, so you get the normal not-operator.
>
Yes, you've remembered correctly. As part of type-checking the
"predicate" class against the "Predicate" concept, the compiler
converts predicate's funny_bool return value to a bool. That way, a
template that was type-checked against Predicate, where operator()
returns a bool, will always be instantiated such that the operator()
call returns a bool. This is crucial to the type-soundness contract
that concepts provide: if a constrained template type-checks, and a
set of template arguments to that template meet its constraints, the
template will instantiate properly [*].
- Doug
[*] There are corner cases involving broken specializations or
overload sets where this won't be the case. They should be extremely
rare in sane code.