Re: Conditionally defining copy and move constructors
Am 10.05.2012 00:05, schrieb Andy Lutomirski:
I have a class template that should be, depending on its parameters,
nontrivially copyable, trivially copyable, or not copyable. It looks
like this:
template<typename Types...>
struct A
{
A(const A&) { body here }
// Members that contain one instance of each type in Types.
};
If the body is ill-formed, all is well -- A can't be copied. If all of
the types are copyable and at least one doesn't have a trivial copy
constructor, all is still well: A is copyable.
I want A to be trivially copyable, though, if all the types are
trivially copyable. The best I've come up with is:
template<typename Types...>
struct helper
{
// if all types are trivially copyable, then
typedef garbage type;
// else
typedef A<Types...> type;
};
template<typename Types...>
struct A
{
A(const typename helper<Types...>::type&) { body here }
// Members that contain one instance of each type in Types.
};
This works until I add a move constructor. Then I end up with:
template<typename Types...>
struct A
{
A(const typename helper<Types...>::type&) { body here }
A(A&&) { ... }
// Members that contain one instance of each type in Types.
};
If the "copy" constructor is a real copy constructor (i.e. helper::type
is A), then it works. If not, though, then this is equivalent to:
template<typename Types...>
struct A
{
A(const garbage&) { body here }
A(A&&) { ... }
// Members that contain one instance of each type in Types.
};
which is not copyable at all. What I really want is:
template<typename Types...>
struct A
{
A(const typename helper<Types...>::type&) { body here }
A(const A&) = default; // But only sometimes
A(A&&) { ... }
// Members that contain one instance of each type in Types.
};
which is ill-formed if helper::type is A.
Any ideas? The default template argument SFINAE trick doesn't work
because copy constructors can't be templates.
There was a similar question in a previous thread ("How to conditionally
disable a copy constructor?" in this newsgroup, about end of November
2011. One approach that works is to use base-classes via CRTP to control
this. From my reply this part may be of interest for you:
#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
{
};
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! ]