The variadic version of std :: is_convertible?

Is it possible to write a variational version of std::is_convertible ? For example, are_convertible<T1, T2, T3, T4> will return is_convertible<T1, T3> && is_convertible<T2, T4> . I thought about this for several hours, but could not think of anything reasonable.

To clarify, I want to use it something like this:

 template <class ...Args1> struct thing { template <class ...Args2> enable_if_t<are_convertible<Args2..., Args1...>::value> foo(Args2 &&...args){} } 
+7
c ++ variadic-templates
source share
3 answers

You do not need to concatenate Args2... and Args1... , and you should not do this, since it makes it impossible to determine where Args2... and Args1... . The way to pass several variational arguments in such a way that they can be extracted separately, wraps them with another template: given the variational pattern my_list you can structure your my_convertible to call

 my_convertible<my_list<Args2...>, my_list<Args1...>> 

The standard library already has a variation template that works well here: tuple . Not only that, but tuple<Args2...> can be converted to tuple<Args1...> if and only if Args2... can be converted to Args1... so you can simply write:

 std::is_convertible<std::tuple<Args2...>, std::tuple<Args1...>> 

Note: in the comments, @ zatm8 reports that this does not always work: std::is_convertible<std::tuple<const char *&&>, std::tuple<std::string &&>>::value reported as false , but std::is_convertible<const char *&&, std::string &&>::value reported as true .

I believe that it is a mistake that both of them should be presented as true . The problem reproduces at http://gcc.godbolt.org/ with clang 3.9.1. It does not play with gcc 6.3, and it also does not play with clang 3.9.1 when using -stdlib=libc++ . It seems that libstdC ++ uses a language function that does not correctly handle clang, and shortening it to a short example that does not depend on standard library headers gives:

 struct S { S(const char *) { } }; int main() { const char *s = ""; static_cast<S &&>(s); } 

This is accepted by gcc but rejected by clang. It is reported that in 2014 https://llvm.org/bugs/show_bug.cgi?id=19917 .

It seems like this was fixed at the end of 2016, but the fix has not yet hit the released version: http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20161031/175955.html

If this has affected you, you can avoid std::tuple and use @Yakk's answer instead.

+11
source share

Yes.

First, how to do it. Then why shouldn't you do it.

How to do it:

Write a rearrangement that takes a list of elements kN and groups it into N groups k alternating. Groups can be template<class...>struct types{}; .

Then write apply, which accepts the groups template<class...>class Z and a class... (aka types<...> ) and applies Z to the contents of each package, returning types<...> result.

Then collapse the contents of types<...> using template<class A, class B> struct and_types:std::integral_constant<bool, A{}&&B{}>{}; .

I would find this mostly meaningless, so I will not implement it. This should be easy with a decent metaprogramming library; most of the above operations are standard.


Why you shouldn't

But actually, given your example, just do the following:

 template<class...Ts> struct and_types:std::true_type{}; template<class T0, class...Ts> struct and_types<T0,Ts...>:std::integral_constant<bool, T0{} && and_types<Ts...>{}>{}; 

Then:

 std::enable_if_t<and_types<std::is_convertible<Args2, Args1>...>{}> 

performs this work. All the shuffles are just noise.

With support for fold ... from C ++ 1z, we also get rid of and_types and just use && and ...

+7
source share

You can use std :: conjuction to collapse all types of results in one:

 template <class... Args> struct is_convertible_multi { constexpr static bool value = false; }; template <class... Args1, class... Args2> struct is_convertible_multi<::std::tuple<Args1...>, ::std::tuple<Args2...>> { constexpr static bool value = ::std::conjunction_v<::std::is_convertible<Args1,Args2>...>; }; 

And don't forget to check

 sizeof... (Args1) == sizeof... (Args2) 

using if constexpr or enable_if

0
source share

All Articles