
On Apr 12, 2006, at 9:25 AM, Peter Dimov wrote:
Doug Gregor wrote:
When we initially type-check lower_bound, we need to determine if there is an advance() that we can call. Both #1 and #2 have been seen, so we check them. Can we call #1? Sure, because every ForwardIterator is also an InputIterator. Can we call #2? Nope, our ForwardIterator is not guaranteed to be a BidirectionalIterator. So, we call #1 our "seed" function and use it for type-checking the template. Type-checking succeeds.
Question #1, what happens when there is no seed?
Type-checking of the template definition fails.
Now, let's consider what happens with this call to lower_bound:
vector<int> v; lower_bound(v.begin(), v.end(), 17);
When instantiating a template that uses concepts, we don't perform the normal second phase lookup with ADL. Instead, we do a more limited lookup designed to make sure that instantiation cannot fail [*]. So, when we instantiate the call to advance() in lower_bound(), we start with our "seed" function #1. We then look for any other functions named "lower_bound" in the same namespace as our seed that are specializations of the seed, and add those functions to our candidate set along with the seed.
Question #2, what happens in the swap() case? The seed (if there's one) is std::swap, but the actual call must go to the specialized swap in the type's namespace.
In that case, you'll need a Swappable concept in your requirements: auto concept Swappable<typename T> { void swap(T&, T&); }; We'll still define std::swap, of course: template<typename T> where CopyConstructible<T> && Assignable<T> void swap(T& x, T& y); The "auto" means that we get implicit matching for the concept, which will either fall back to the swap() definition above or will find a more specialized swap in the type's namespace. Doug