unique_ptr<T>requiresT*to be aNullablePointer[unique.ptr]p3NullablePointerrequires lvalues ofT*to beSwappable[nullablepointer.requirements]p1Swappableessentially requiresusing std::swap; swap(x, y);to select an overload forx,ybeing lvalues of typeT*[swappable.requirements]p3
In the last step, your type foo::bar produces an ambiguity and therefore violates the requirements of unique_ptr. libstdc++’s implementation is conforming, although I’d say this is rather surprising.
The wording is of course a bit more convoluted, because it is generic.
[unique.ptr]p3
If the type
remove_reference_t<D>::pointerexists,
thenunique_ptr<T, D>::pointershall be a synonym for
remove_reference_t<D>::pointer. Otherwiseunique_ptr<T,shall be a synonym for
D>::pointerT*. The typeunique_ptr<T,shall satisfy the requirements of
D>::pointerNullablePointer.
(emphasis mine)
[nullablepointer.requirements]p1
A
NullablePointertype is a pointer-like type that supports null
values. A typePmeets the requirements ofNullablePointerif:
- […]
- lvalues of type
Pare swappable (17.6.3.2),- […]
[swappable.requirements]p2
An object
tis swappable with an objectuif and only if:
- the expressions
swap(t, u)andswap(u, t)are valid when evaluated in the context described below, and- […]
[swappable.requirements]p3
The context in which
swap(t, u)andswap(u, t)are evaluated shall
ensure that a binary non-member function named “swap” is selected via
overload resolution on a candidate set that includes:
- the two
swapfunction templates defined in<utility>and- the lookup set produced by argument-dependent lookup.
Note that for a pointer type T*, for purposes of ADL, the associated namespaces and classes are derived from the type T. Hence, foo::bar* has foo as an associated namespace. ADL for swap(x, y) where either x or y is a foo::bar* will therefore find foo::swap.