Boost logo

Boost :

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.


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