Re: How to conditionally disable a copy constructor?
Am 23.11.2011 17:08, schrieb Daniel Kr??gler:
Am 22.11.2011 20:44, schrieb Andrzej Krzemie??ski:
I am trying to implement a template "wrapper" for any arbitrary type,
much like boost::optional. In fact I am trying to write my own
alternative implementation of optional. I want it to have the
following property: if the wrapped type T ptovides copy constructor,
optional<T> also provides a copy constructor; similarly, optional<T>
provides a move constructor iff T provides one. With concepts (as
defined in N2914), I would express it like this:
[..]
I just realize that it probably does not. While this approach realized
that Optional<X> has the same copy/move construction as type X, it does
still not describe how to *define* the corresponding member function
*iff* T does have an available member.
You need to extend my first approach by replacing copy_move1 and
copy_move2 by class templates, that implement the copy and move
semantics of T acting on the opaque buffer as required by the semantics
of Optional. This should still give you the advantage that you don't
need to specialize Optional completely.
I couldn't resist to implement the idea of combining CRTP and policy
base classes: Note that the following code restricts to copy/move
construction. The approach for copy/move assignment is absolutely
analogous. Enjoy!
#include <cassert>
#include <type_traits>
#include <utility>
#include <new>
struct no_move_no_copy
{
no_move_no_copy() = default;
no_move_no_copy(const no_move_no_copy&) = delete;
no_move_no_copy(no_move_no_copy&&) = delete;
};
template<class T, class D>
struct copy_move
{
copy_move() = default;
copy_move(const copy_move& rhs)
noexcept(std::is_nothrow_copy_constructible<T>::value)
{
static_cast<D&>(*this).copy_construct(static_cast<const D&>(rhs));
}
copy_move(copy_move&& rhs)
noexcept(std::is_nothrow_move_constructible<T>::value)
{
static_cast<D&>(*this).move_construct(std::move(static_cast<D&>(rhs)));
}
};
template<class T, class D>
struct copy_no_move
{
copy_no_move() = default;
copy_no_move(const copy_no_move& rhs)
noexcept(std::is_nothrow_copy_constructible<T>::value)
{
static_cast<D&>(*this).copy_construct(static_cast<const D&>(rhs));
}
};
template<class T, class D>
struct move_no_copy
{
move_no_copy() = default;
move_no_copy(move_no_copy&& rhs)
noexcept(std::is_nothrow_move_constructible<T>::value)
{
static_cast<D&>(*this).move_construct(std::move(static_cast<D&>(rhs)));
}
};
template<class T, class D>
struct select_optional_base :
std::conditional<
std::is_copy_constructible<T>::value,
std::conditional<std::is_move_constructible<T>::value,
copy_move<T, D>,
copy_no_move<T, D>>,
std::conditional<std::is_move_constructible<T>::value,
move_no_copy<T, D>,
no_move_no_copy>
>::type
{
};
template<class T>
class optional_data
{
using raw_type = typename std::aligned_storage<sizeof(T)>::type;
bool is_init_;
raw_type raw_data_;
public:
optional_data() noexcept : is_init_(false) {}
optional_data(const optional_data& rhs) noexcept : is_init_(false) {}
optional_data(optional_data&& rhs) noexcept : is_init_(false) {}
~optional_data()
{
this->destroy();
}
auto is_initialized() const noexcept -> bool
{
return this->is_init_;
}
auto raw_memory() noexcept -> void*
{
return &this->raw_data_;
}
auto raw_memory() const noexcept -> const void*
{
return &this->raw_data_;
}
auto data() noexcept -> T&
{
return *static_cast<T*>(raw_memory());
}
auto data() const noexcept -> const T&
{
return *static_cast<const T*>(raw_memory());
}
template<class... U>
void construct(U&&... u)
{
assert(!this->is_initialized());
::new (this->raw_memory()) T(std::forward<U>(u)...);
this->is_init_ = true;
}
void destroy()
{
if (this->is_init_)
{
data().~T();
this->is_init_ = false;
}
}
};
template<class P1, class P2>
struct and_ : std::conditional<P1::value, P1, P2>::type
{
};
template<class P>
struct not_ : std::integral_constant<bool, !P::value>
{
};
template<class, class...>
struct is_self_construct : std::false_type
{
};
template<class T, class U>
struct is_self_construct<T, U> : std::is_base_of<
typename std::remove_reference<U>::type, T
{
};
template<typename T>
class Optional : private optional_data<T>,
private select_optional_base<T, Optional<T>>::type
{
using copy_type = typename select_optional_base<T, Optional<T>>::type;
friend copy_type;
void copy_construct(const Optional& rhs)
{
if (rhs.is_initialized())
{
this->construct(rhs.data());
}
}
void move_construct(Optional&& rhs)
{
if (rhs.is_initialized())
{
this->construct(std::move(rhs.data()));
}
}
public:
Optional() = default;
template<class... U,
class = typename std::enable_if<
and_<
std::is_constructible<T, U...>,
not_<is_self_construct<Optional<T>, U...>>
>::value
>::type
>
Optional(U&&... u)
{
this->construct(std::forward<U>(u)...);
}
auto operator*() -> T&
{
assert(this->is_initialized());
return this->data();
}
auto operator*() const -> const T&
{
assert(this->is_initialized());
return this->data();
}
explicit operator bool() const noexcept
{
return this->is_initialized();
}
};
HTH & Greetings from Bremen,
Daniel Kr??gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]