
On Tue, Sep 28, 2010 at 1:04 PM, Sebastian Redl <sebastian.redl@getdesigned.at> 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.