Here is a test file from gcc, live demo
struct do_nothing
{
template <class T>
void operator()(T*) {}
};
int
main()
{
int i = 0;
std::unique_ptr<int, do_nothing> p1(&i);
std::unique_ptr<int> p2;
static_assert(!std::is_assignable<decltype(p2), decltype(p1)>::value, ""); // note ! here.
}
std::is_assignable
If the expression
std::declval<T>() = std::declval<U>()
is well-formed in unevaluated context, provides the member constant value equal true. Otherwise, value is false. Access checks are performed as if from a context unrelated to either type.
std::declval
:
template<class T>
typename std::add_rvalue_reference<T>::type declval() noexcept;
The return type is
T&&
unlessT
is (possibly cv-qualified) void, in which case the return type is T.
Let's look at MoveAssignOnly
:
struct MoveAssignOnly {
MoveAssignOnly &operator=(MoveAssignOnly &) = delete;
MoveAssignOnly &operator=(MoveAssignOnly &&) = default;
};
int main()
{
static_assert(
not std::is_assignable<MoveAssignOnly, MoveAssignOnly>::value, "");
}
live demo:
error: static_assert failed due to requirement '!std::is_assignable<MoveAssignOnly, MoveAssignOnly>::value'
Yes, it fails to compile because it provides a move assignment
Let's return to the gcc's test file and std::unique_ptr
. As we know, std::unique_ptr
also has move assignments.
However, unlike struct MoveAssignOnly
, static_assert(!std::is_assignable<decltype(p2), decltype(p1)>::value, "");
(more clearly, static_assert(!std::is_assignable<std::unique_ptr<int>, std::unique_ptr<int, do_nothing>>::value, "");
compiles happily.
I have struggled with libcxx's implementation of unique_ptr
for long time, but still cannot figure out: how can std::unique_ptr
be not assignable(! is_assignable
) when std::unique_ptr
provides move assignments?
p1
and p2
are of a different type. Unlike with shared_ptr
, the deleter of a unique_ptr
is part of the pointer's type. This means the move assignment operator does not allow you to assign (even move-assign) between two unique_ptr
s if their deleter types differ.
unique_ptr
also offers an assignment operator template which allows assigning from an rvalue of unique_ptr
with a different deleter, but the deleters must be assignable (see reference). So you can make your static assert fire by making the deleters assignable:
struct do_nothing
{
template <class T>
void operator()(T*) {}
template <class T>
operator std::default_delete<T>() { return {}; }
};
int
main()
{
int i = 0;
std::unique_ptr<int, do_nothing> p1(&i);
std::unique_ptr<int> p2;
static_assert(!std::is_assignable<decltype(p2), decltype(p1)>::value, ""); // note ! here.
}
[Live example]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With