#include <vector>
#include <memory>
template<typename T>
class V {
public:
template<typename U = T, std::enable_if_t<std::is_copy_assignable_v<U>, int> = 0>
auto operator = (const V &rhs) -> V & { v = rhs.v; return *this; }
private:
std::vector<T> v;
};
template<typename T>
class U {
public:
template<typename U = T, std::enable_if_t<std::is_copy_assignable_v<U>, int> = 0>
auto operator = (const U &rhs) -> U & { t = rhs.t; return *this; }
private:
T t;
};
int main()
{
static_assert(!std::is_copy_assignable_v<std::unique_ptr<int>>); // success
static_assert(!std::is_copy_assignable_v<U<std::unique_ptr<int>>>); // success
static_assert(!std::is_copy_assignable_v<V<std::unique_ptr<int>>>); // fail
return 0;
}
Here, U<T>
and V<T>
have assignment operator when T
is copy-assignable.
However, static_assert
to check if V<std::unique_ptr<int>>
is non-copy-assignable fails although similar check for U<std::unique_ptr<int>>
successes.
Why does the static_assert
fail for V<T>
here and how can I fix it?
CodePudding user response:
Since C 20 this can be done simpler than using a conditional base, instead using a requires
clause to enable/disable the default implementation of the copy assignment:
template<typename T>
class V {
public:
V(const V&) requires std::is_copy_constructible_v<T> = default;
V(V&&) = default;
auto operator=(const V&) -> V& requires std::is_copy_assignable_v<T> = default;
auto operator=(V&&) -> V& = default;
private:
std::vector<T> v;
};
Although I think you really want (std::is_copy_assignable_v<T> && std::is_copy_constructible_v<T>)
because the copy assignment of std::vector<T>
may be using either.
CodePudding user response:
The special copy assignment operator is never a template. In cpp insights we can see that the compiler will generate a non-template copy assignment operator in addition to the template operator you provided.
You can conditionally disable special member functions of template class based on template parameter by inheriting from a base class with desired semantics
#include <type_traits>
class copyable { };
class no_copy_assign { void operator=(const no_copy_assign&) = delete; };
template<class T>
class A :
std::conditional_t</* Condition */, copyable, no_copy_assign>
{ };
// Condition can be std::is_copy_assignable_v<T>
CodePudding user response:
This is how I would do it, with a constexpr function to evaluate copyable conditions. Compile time test code attached.
#include <type_traits>
class copyable
{
};
class non_copyable
{
public:
non_copyable(const non_copyable&) = delete;
non_copyable& operator=(const non_copyable&) = delete;
};
template<typename type_t>
constexpr bool is_copyable()
{
return
std::is_copy_constructible_v<type_t> &&
std::is_copy_assignable_v<type_t>;
}
template<typename type_t>
class my_class_t :
public std::conditional_t<is_copyable<type_t>(), copyable, non_copyable>
{
};
class test
{
public:
test() = default;
~test() = default;
test(const test&) = delete;
test& operator=(const test&) = delete;
};
int main()
{
static_assert(!std::is_copy_constructible_v<test>);
static_assert(!std::is_copy_constructible_v<my_class_t<test>>);
return 0;
}