3

I have different view types, which each have a std::size_t View::dimension member constant, and a typename View::value_type member type.

The following compile-type check should verify if both From and To are views (verified using is_view<>), and the content of From can be assigned to To. (same dimensions, and convertible value types).

template<typename From, typename To> struct is_compatible_view : std::integral_constant<bool, is_view<From>::value && is_view<To>::value && From::dimension == To::dimension && std::is_convertible<typename From::value_type, typename To::value_type>::value > { }; 

is_view<T> is such that it always evaluates to std::true_type or std::false_type, for any type T. The problem is that if From or To is not a view type, then From::dimension (for example) may not exist, and is_compatible_view<From, To> causes a compilation error. It should instead evaluate to std::false_type in this case.

is_compatible_view is used for SFINAE with std::enable_if, to disable member functions. For example a view class can have a member function

struct View { constexpr static std::size_t dimension = ... using value_type = ... template<typename Other_view> std::enable_if_t<is_compatible_view<Other_view, View>> assign_from(const Other_view&); void assign_from(const Not_a_view&); }; 

Not_a_view is not a view, and causes a compilation error in is_compatible_view<Not_a_view, ...>. When calling view.assign_from(Not_a_view()), SFINAE does not apply, and instead a compilation error occurs when the compiler tries to resolve the first assign_from function.

How can is_compatible_view be written such that this works correctly? In C++17 does std::conjunction<...> allow this?

1 Answer 1

4

One approach is using something like std::conditional to delay evaluation of some parts of your type trait until we've verified that other parts of your type trait are already true.

That is:

// this one is only valid if From and To are views template <class From, class To> struct is_compatible_view_details : std::integral_constant<bool, From::dimension == To::dimension && std::is_convertible<typename From::value_type, typename To::value_type>::value > { }; // this is the top level one template<typename From, typename To> struct is_compatible_view : std::conditional_t< is_view<From>::value && is_view<To>::value, is_compatible_view_details<From, To>, std::false_type>::type { }; 

Note that I'm using both conditional_t and ::type. is_compatible_view_details will only be instantiated if both From and To are views.


A similar approach would be to use std::conjunction with the above, which because of short-circuiting will similarly delay evaluation:

template <class From, class To> struct is_compatible_view : std::conjunction_t< is_view<From>, is_view<To>, is_compatible_view_details<From, To> > { }; 

Either way, you need to pull out the details.


A third approach would be to use enable_if_t as a specialization:

template <class From, class To, class = void> struct is_compatible_view : std::false_type { }; template <class From, class To> struct is_compatible_view<From, To, std::enable_if_t< is_view<From>::value && is_view<To>::value && From::dimension == To::dimension && std::is_convertible<typename From::value_type, typename To::value_type>::value>> : std::true_type { }; 

Here, if any of the expressions in the enable_if_t are ill-formed, SFINAE kicks in and we just use the primary template, which is false_type.

Sign up to request clarification or add additional context in comments.

1 Comment

I actually don't really see the point of the ::type in your first snippet. Also, depending on the use case, it may suffice to make an enable_if_compatible_view alias template which just pull any errors in From::value_type etc. into the immediate context.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.