I'm trying to write my own version of std::is_copy_constructible that will return false for containers of move-only types.
I already found this answer describing how to make this work for a specific container type. Now I'm trying to expand that to work for any Container. For my purposes I am defining "Container" to mean any class that has a 'value_type' member. To determine whether or not a class has a 'value_type' member, I am using a variant on this this answer.
So basically what I have right now is this:
template <typename T, typename = void>
struct has_value_type : std::false_type {};
template <typename T>
struct has_value_type<T, decltype(std::declval<T>().value_type, void())> : std::true_type {};
for determining whether a class has a value_type member, and this:
template <template <typename> class test, typename T>
struct smart_test : test<T> {};
template <template <typename> class test, typename T, typename A>
struct smart_test<test, std::vector<T, A>> : smart_test<test, T> {};
template <typename T>
using smart_is_copy_constructible = smart_test<std::is_copy_constructible, T>;
for defining smart_is_copy_constructible that works correctly for vectors and non-container types.
I want to combine the two to create a version of smart_is_copy_constructible that works correctly for all container types (types with a value_type member). I suspect I need to use std::enable_if with my has_value_type struct in some way to do this, but this is my first foray into template metaprogramming and I'm not sure how to proceed.
My best guess so far was to try this
template <template <typename> class test, typename T>
struct smart_test<test, std::enable_if_t<has_value_type<T>::value> : smart_test<test, typename T::value_type> {};
Instead of the second declaration from the previous code block (the one defining a version of smart_test specialized on vector), but this fails to compile.
Any help would be greatly appreciated!
Yes, std::enable_if_t
will do it:
// I'd recommend following the standard library's naming convention
template<typename T, typename = void>
struct smart_is_copy_constructible : std::is_copy_constructible<T> { };
template<typename T>
struct smart_is_copy_constructible<T, std::enable_if_t<has_value_type<T>::value>> : std::is_copy_constructible<typename T::value_type> { };
template<typename T>
constexpr bool smart_is_copy_constructible_v = smart_is_copy_constructible<T>::value;
But has_value_type
is mostly superfluous. Your implementation doesn't work, and the implementation I gave in the comments is short enough to inline (though, as Nicol Bolas suggests, you may want to make has_value_type
more complicated):
template<typename T, typename = void>
struct smart_is_copy_constructible : std::is_copy_constructible<T> { };
template<typename T>
struct smart_is_copy_constructible<T, std::void_t<typename T::value_type>> : std::is_copy_constructible<typename T::value_type> { };
(The corrected has_value_type
as used in the first example is
template <typename T, typename = void>
struct has_value_type : std::false_type {};
template <typename T>
struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type {};
)