unique_ptr<T>
requiresT*
to be aNullablePointer
[unique.ptr]p3NullablePointer
requires lvalues ofT*
to beSwappable
[nullablepointer.requirements]p1Swappable
essentially requiresusing std::swap; swap(x, y);
to select an overload forx
,y
being 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>::pointer
exists,
thenunique_ptr<T, D>::pointer
shall 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
NullablePointer
type is a pointer-like type that supports null
values. A typeP
meets the requirements ofNullablePointer
if:
- […]
- lvalues of type
P
are swappable (17.6.3.2),- […]
[swappable.requirements]p2
An object
t
is swappable with an objectu
if 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
swap
function 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
.