forked from mirror/libcxx
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
421 lines
13 KiB
C++
421 lines
13 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
|
// Source Licenses. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// UNSUPPORTED: c++98, c++03
|
|
|
|
// <memory>
|
|
|
|
// unique_ptr
|
|
|
|
// Test unique_ptr converting move ctor
|
|
|
|
#include <memory>
|
|
#include <cassert>
|
|
|
|
#include "test_macros.h"
|
|
#include "unique_ptr_test_helper.h"
|
|
#include "type_id.h"
|
|
|
|
template <int ID = 0>
|
|
struct GenericDeleter {
|
|
void operator()(void*) const {}
|
|
};
|
|
|
|
template <int ID = 0>
|
|
struct GenericConvertingDeleter {
|
|
|
|
template <int OID>
|
|
GenericConvertingDeleter(GenericConvertingDeleter<OID>) {}
|
|
|
|
template <int OID>
|
|
GenericConvertingDeleter& operator=(GenericConvertingDeleter<OID> const&) {
|
|
return *this;
|
|
}
|
|
|
|
void operator()(void*) const {}
|
|
};
|
|
|
|
template <class T, class U>
|
|
using EnableIfNotSame = typename std::enable_if<
|
|
!std::is_same<typename std::decay<T>::type, typename std::decay<U>::type>::value
|
|
>::type;
|
|
|
|
template <class Templ, class Other>
|
|
struct is_specialization;
|
|
|
|
template <template <int> class Templ, int ID1, class Other>
|
|
struct is_specialization<Templ<ID1>, Other> : std::false_type {};
|
|
|
|
template <template <int> class Templ, int ID1, int ID2>
|
|
struct is_specialization<Templ<ID1>, Templ<ID2> > : std::true_type {};
|
|
|
|
template <class Templ, class Other>
|
|
using EnableIfSpecialization = typename std::enable_if<
|
|
is_specialization<Templ, typename std::decay<Other>::type >::value
|
|
>::type;
|
|
|
|
template <int ID> struct TrackingDeleter;
|
|
template <int ID> struct ConstTrackingDeleter;
|
|
|
|
template <int ID>
|
|
struct TrackingDeleter {
|
|
TrackingDeleter() : arg_type(&makeArgumentID<>()) {}
|
|
|
|
TrackingDeleter(TrackingDeleter const&)
|
|
: arg_type(&makeArgumentID<TrackingDeleter const&>()) {}
|
|
|
|
TrackingDeleter(TrackingDeleter&&)
|
|
: arg_type(&makeArgumentID<TrackingDeleter &&>()) {}
|
|
|
|
template <class T, class = EnableIfSpecialization<TrackingDeleter, T> >
|
|
TrackingDeleter(T&&) : arg_type(&makeArgumentID<T&&>()) {}
|
|
|
|
TrackingDeleter& operator=(TrackingDeleter const&) {
|
|
arg_type = &makeArgumentID<TrackingDeleter const&>();
|
|
return *this;
|
|
}
|
|
|
|
TrackingDeleter& operator=(TrackingDeleter &&) {
|
|
arg_type = &makeArgumentID<TrackingDeleter &&>();
|
|
return *this;
|
|
}
|
|
|
|
template <class T, class = EnableIfSpecialization<TrackingDeleter, T> >
|
|
TrackingDeleter& operator=(T&&) {
|
|
arg_type = &makeArgumentID<T&&>();
|
|
return *this;
|
|
}
|
|
|
|
void operator()(void*) const {}
|
|
|
|
public:
|
|
TypeID const* reset() const {
|
|
TypeID const* tmp = arg_type;
|
|
arg_type = nullptr;
|
|
return tmp;
|
|
}
|
|
|
|
mutable TypeID const* arg_type;
|
|
};
|
|
|
|
template <int ID>
|
|
struct ConstTrackingDeleter {
|
|
ConstTrackingDeleter() : arg_type(&makeArgumentID<>()) {}
|
|
|
|
ConstTrackingDeleter(ConstTrackingDeleter const&)
|
|
: arg_type(&makeArgumentID<ConstTrackingDeleter const&>()) {}
|
|
|
|
ConstTrackingDeleter(ConstTrackingDeleter&&)
|
|
: arg_type(&makeArgumentID<ConstTrackingDeleter &&>()) {}
|
|
|
|
template <class T, class = EnableIfSpecialization<ConstTrackingDeleter, T> >
|
|
ConstTrackingDeleter(T&&) : arg_type(&makeArgumentID<T&&>()) {}
|
|
|
|
const ConstTrackingDeleter& operator=(ConstTrackingDeleter const&) const {
|
|
arg_type = &makeArgumentID<ConstTrackingDeleter const&>();
|
|
return *this;
|
|
}
|
|
|
|
const ConstTrackingDeleter& operator=(ConstTrackingDeleter &&) const {
|
|
arg_type = &makeArgumentID<ConstTrackingDeleter &&>();
|
|
return *this;
|
|
}
|
|
|
|
template <class T, class = EnableIfSpecialization<ConstTrackingDeleter, T> >
|
|
const ConstTrackingDeleter& operator=(T&&) const {
|
|
arg_type = &makeArgumentID<T&&>();
|
|
return *this;
|
|
}
|
|
|
|
void operator()(void*) const {}
|
|
|
|
public:
|
|
TypeID const* reset() const {
|
|
TypeID const* tmp = arg_type;
|
|
arg_type = nullptr;
|
|
return tmp;
|
|
}
|
|
|
|
mutable TypeID const* arg_type;
|
|
};
|
|
|
|
template <class ExpectT, int ID>
|
|
bool checkArg(TrackingDeleter<ID> const& d) {
|
|
return d.arg_type && *d.arg_type == makeArgumentID<ExpectT>();
|
|
}
|
|
|
|
template <class ExpectT, int ID>
|
|
bool checkArg(ConstTrackingDeleter<ID> const& d) {
|
|
return d.arg_type && *d.arg_type == makeArgumentID<ExpectT>();
|
|
}
|
|
|
|
template <class From, bool AssignIsConst = false>
|
|
struct AssignDeleter {
|
|
AssignDeleter() = default;
|
|
AssignDeleter(AssignDeleter const&) = default;
|
|
AssignDeleter(AssignDeleter&&) = default;
|
|
|
|
AssignDeleter& operator=(AssignDeleter const&) = delete;
|
|
AssignDeleter& operator=(AssignDeleter &&) = delete;
|
|
|
|
template <class T> AssignDeleter& operator=(T&&) && = delete;
|
|
template <class T> AssignDeleter& operator=(T&&) const && = delete;
|
|
|
|
template <class T, class = typename std::enable_if<
|
|
std::is_same<T&&, From>::value && !AssignIsConst
|
|
>::type>
|
|
AssignDeleter& operator=(T&&) & { return *this; }
|
|
|
|
template <class T, class = typename std::enable_if<
|
|
std::is_same<T&&, From>::value && AssignIsConst
|
|
>::type>
|
|
const AssignDeleter& operator=(T&&) const & { return *this; }
|
|
|
|
template <class T>
|
|
void operator()(T) const {}
|
|
};
|
|
|
|
template <class VT, class DDest, class DSource>
|
|
void doDeleterTest() {
|
|
using U1 = std::unique_ptr<VT, DDest>;
|
|
using U2 = std::unique_ptr<VT, DSource>;
|
|
static_assert(std::is_nothrow_assignable<U1, U2&&>::value, "");
|
|
typename std::decay<DDest>::type ddest;
|
|
typename std::decay<DSource>::type dsource;
|
|
U1 u1(nullptr, ddest);
|
|
U2 u2(nullptr, dsource);
|
|
u1 = std::move(u2);
|
|
}
|
|
|
|
template <bool IsArray>
|
|
void test_sfinae() {
|
|
typedef typename std::conditional<IsArray, A[], A>::type VT;
|
|
|
|
{ // Test that different non-reference deleter types are allowed so long
|
|
// as they convert to each other.
|
|
using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> >;
|
|
using U2 = std::unique_ptr<VT, GenericConvertingDeleter<1> >;
|
|
static_assert(std::is_assignable<U1, U2&&>::value, "");
|
|
}
|
|
{ // Test that different non-reference deleter types are disallowed when
|
|
// they cannot convert.
|
|
using U1 = std::unique_ptr<VT, GenericDeleter<0> >;
|
|
using U2 = std::unique_ptr<VT, GenericDeleter<1> >;
|
|
static_assert(!std::is_assignable<U1, U2&&>::value, "");
|
|
}
|
|
{ // Test that if the deleter assignment is not valid the assignment operator
|
|
// SFINAEs.
|
|
using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> const& >;
|
|
using U2 = std::unique_ptr<VT, GenericConvertingDeleter<0> >;
|
|
using U3 = std::unique_ptr<VT, GenericConvertingDeleter<0> &>;
|
|
using U4 = std::unique_ptr<VT, GenericConvertingDeleter<1> >;
|
|
using U5 = std::unique_ptr<VT, GenericConvertingDeleter<1> const&>;
|
|
static_assert(!std::is_assignable<U1, U2&&>::value, "");
|
|
static_assert(!std::is_assignable<U1, U3&&>::value, "");
|
|
static_assert(!std::is_assignable<U1, U4&&>::value, "");
|
|
static_assert(!std::is_assignable<U1, U5&&>::value, "");
|
|
|
|
using U1C = std::unique_ptr<const VT, GenericConvertingDeleter<0> const&>;
|
|
static_assert(std::is_nothrow_assignable<U1C, U1&&>::value, "");
|
|
}
|
|
{ // Test that if the deleter assignment is not valid the assignment operator
|
|
// SFINAEs.
|
|
using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> & >;
|
|
using U2 = std::unique_ptr<VT, GenericConvertingDeleter<0> >;
|
|
using U3 = std::unique_ptr<VT, GenericConvertingDeleter<0> &>;
|
|
using U4 = std::unique_ptr<VT, GenericConvertingDeleter<1> >;
|
|
using U5 = std::unique_ptr<VT, GenericConvertingDeleter<1> const&>;
|
|
|
|
static_assert(std::is_nothrow_assignable<U1, U2&&>::value, "");
|
|
static_assert(std::is_nothrow_assignable<U1, U3&&>::value, "");
|
|
static_assert(std::is_nothrow_assignable<U1, U4&&>::value, "");
|
|
static_assert(std::is_nothrow_assignable<U1, U5&&>::value, "");
|
|
|
|
using U1C = std::unique_ptr<const VT, GenericConvertingDeleter<0> &>;
|
|
static_assert(std::is_nothrow_assignable<U1C, U1&&>::value, "");
|
|
}
|
|
{ // Test that non-reference destination deleters can be assigned
|
|
// from any source deleter type with a sutible conversion. Including
|
|
// reference types.
|
|
using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> >;
|
|
using U2 = std::unique_ptr<VT, GenericConvertingDeleter<0> &>;
|
|
using U3 = std::unique_ptr<VT, GenericConvertingDeleter<0> const &>;
|
|
using U4 = std::unique_ptr<VT, GenericConvertingDeleter<1> >;
|
|
using U5 = std::unique_ptr<VT, GenericConvertingDeleter<1> &>;
|
|
using U6 = std::unique_ptr<VT, GenericConvertingDeleter<1> const&>;
|
|
static_assert(std::is_assignable<U1, U2&&>::value, "");
|
|
static_assert(std::is_assignable<U1, U3&&>::value, "");
|
|
static_assert(std::is_assignable<U1, U4&&>::value, "");
|
|
static_assert(std::is_assignable<U1, U5&&>::value, "");
|
|
static_assert(std::is_assignable<U1, U6&&>::value, "");
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
{
|
|
using Del = GenericDeleter<0>;
|
|
using AD = AssignDeleter<Del&&>;
|
|
using ADC = AssignDeleter<Del&&, /*AllowConstAssign*/true>;
|
|
doDeleterTest<VT, AD, Del>();
|
|
doDeleterTest<VT, AD&, Del>();
|
|
doDeleterTest<VT, ADC const&, Del>();
|
|
}
|
|
{
|
|
using Del = GenericDeleter<0>;
|
|
using AD = AssignDeleter<Del&>;
|
|
using ADC = AssignDeleter<Del&, /*AllowConstAssign*/true>;
|
|
doDeleterTest<VT, AD, Del&>();
|
|
doDeleterTest<VT, AD&, Del&>();
|
|
doDeleterTest<VT, ADC const&, Del&>();
|
|
}
|
|
{
|
|
using Del = GenericDeleter<0>;
|
|
using AD = AssignDeleter<Del const&>;
|
|
using ADC = AssignDeleter<Del const&, /*AllowConstAssign*/true>;
|
|
doDeleterTest<VT, AD, Del const&>();
|
|
doDeleterTest<VT, AD&, Del const&>();
|
|
doDeleterTest<VT, ADC const&, Del const&>();
|
|
}
|
|
}
|
|
|
|
|
|
template <bool IsArray>
|
|
void test_noexcept() {
|
|
typedef typename std::conditional<IsArray, A[], A>::type VT;
|
|
{
|
|
typedef std::unique_ptr<const VT> APtr;
|
|
typedef std::unique_ptr<VT> BPtr;
|
|
static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, "");
|
|
}
|
|
{
|
|
typedef std::unique_ptr<const VT, CDeleter<const VT> > APtr;
|
|
typedef std::unique_ptr<VT, CDeleter<VT> > BPtr;
|
|
static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, "");
|
|
}
|
|
{
|
|
typedef std::unique_ptr<const VT, NCDeleter<const VT>&> APtr;
|
|
typedef std::unique_ptr<VT, NCDeleter<const VT>&> BPtr;
|
|
static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, "");
|
|
}
|
|
{
|
|
typedef std::unique_ptr<const VT, const NCConstDeleter<const VT>&> APtr;
|
|
typedef std::unique_ptr<VT, const NCConstDeleter<const VT>&> BPtr;
|
|
static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, "");
|
|
}
|
|
}
|
|
|
|
template <bool IsArray>
|
|
void test_deleter_value_category() {
|
|
typedef typename std::conditional<IsArray, A[], A>::type VT;
|
|
using TD1 = TrackingDeleter<1>;
|
|
using TD2 = TrackingDeleter<2>;
|
|
TD1 d1;
|
|
TD2 d2;
|
|
using CD1 = ConstTrackingDeleter<1>;
|
|
using CD2 = ConstTrackingDeleter<2>;
|
|
CD1 cd1;
|
|
CD2 cd2;
|
|
|
|
{ // Test non-reference deleter conversions
|
|
using U1 = std::unique_ptr<VT, TD1 >;
|
|
using U2 = std::unique_ptr<VT, TD2 >;
|
|
U1 u1;
|
|
U2 u2;
|
|
u1.get_deleter().reset();
|
|
u1 = std::move(u2);
|
|
assert(checkArg<TD2&&>(u1.get_deleter()));
|
|
}
|
|
{ // Test assignment to non-const ref
|
|
using U1 = std::unique_ptr<VT, TD1& >;
|
|
using U2 = std::unique_ptr<VT, TD2 >;
|
|
U1 u1(nullptr, d1);
|
|
U2 u2;
|
|
u1.get_deleter().reset();
|
|
u1 = std::move(u2);
|
|
assert(checkArg<TD2&&>(u1.get_deleter()));
|
|
}
|
|
{ // Test assignment to const&.
|
|
using U1 = std::unique_ptr<VT, CD1 const& >;
|
|
using U2 = std::unique_ptr<VT, CD2 >;
|
|
U1 u1(nullptr, cd1);
|
|
U2 u2;
|
|
u1.get_deleter().reset();
|
|
u1 = std::move(u2);
|
|
assert(checkArg<CD2&&>(u1.get_deleter()));
|
|
}
|
|
|
|
{ // Test assignment from non-const ref
|
|
using U1 = std::unique_ptr<VT, TD1 >;
|
|
using U2 = std::unique_ptr<VT, TD2& >;
|
|
U1 u1;
|
|
U2 u2(nullptr, d2);
|
|
u1.get_deleter().reset();
|
|
u1 = std::move(u2);
|
|
assert(checkArg<TD2&>(u1.get_deleter()));
|
|
}
|
|
{ // Test assignment from const ref
|
|
using U1 = std::unique_ptr<VT, TD1 >;
|
|
using U2 = std::unique_ptr<VT, TD2 const& >;
|
|
U1 u1;
|
|
U2 u2(nullptr, d2);
|
|
u1.get_deleter().reset();
|
|
u1 = std::move(u2);
|
|
assert(checkArg<TD2 const&>(u1.get_deleter()));
|
|
}
|
|
|
|
{ // Test assignment from non-const ref
|
|
using U1 = std::unique_ptr<VT, TD1& >;
|
|
using U2 = std::unique_ptr<VT, TD2& >;
|
|
U1 u1(nullptr, d1);
|
|
U2 u2(nullptr, d2);
|
|
u1.get_deleter().reset();
|
|
u1 = std::move(u2);
|
|
assert(checkArg<TD2&>(u1.get_deleter()));
|
|
}
|
|
{ // Test assignment from const ref
|
|
using U1 = std::unique_ptr<VT, TD1& >;
|
|
using U2 = std::unique_ptr<VT, TD2 const& >;
|
|
U1 u1(nullptr, d1);
|
|
U2 u2(nullptr, d2);
|
|
u1.get_deleter().reset();
|
|
u1 = std::move(u2);
|
|
assert(checkArg<TD2 const&>(u1.get_deleter()));
|
|
}
|
|
|
|
{ // Test assignment from non-const ref
|
|
using U1 = std::unique_ptr<VT, CD1 const& >;
|
|
using U2 = std::unique_ptr<VT, CD2 & >;
|
|
U1 u1(nullptr, cd1);
|
|
U2 u2(nullptr, cd2);
|
|
u1.get_deleter().reset();
|
|
u1 = std::move(u2);
|
|
assert(checkArg<CD2 &>(u1.get_deleter()));
|
|
}
|
|
{ // Test assignment from const ref
|
|
using U1 = std::unique_ptr<VT, CD1 const& >;
|
|
using U2 = std::unique_ptr<VT, CD2 const& >;
|
|
U1 u1(nullptr, cd1);
|
|
U2 u2(nullptr, cd2);
|
|
u1.get_deleter().reset();
|
|
u1 = std::move(u2);
|
|
assert(checkArg<CD2 const&>(u1.get_deleter()));
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
{
|
|
test_sfinae</*IsArray*/false>();
|
|
test_noexcept<false>();
|
|
test_deleter_value_category<false>();
|
|
}
|
|
{
|
|
test_sfinae</*IsArray*/true>();
|
|
test_noexcept<true>();
|
|
test_deleter_value_category<true>();
|
|
}
|
|
}
|