问题
Is it possible to write a variadic version of std::is_convertible
? For example are_convertible<T1, T2, T3, T4>
would return is_convertible<T1, T3> && is_convertible<T2, T4>
. I've been thinking about this for a few hours but couldn't come up with anything reasonable.
To clarify I want to use it somewhat like this:
template <class ...Args1>
struct thing
{
template <class ...Args2>
enable_if_t<are_convertible<Args2..., Args1...>::value>
foo(Args2 &&...args){}
}
回答1:
You don't need to concatenate Args2...
and Args1...
, and you shouldn't, since doing to makes it makes it impossible to tell where Args2...
ends and Args1...
begins. The way to pass multiple variadic arguments in a way that allows them to be extracted individually is wrapping them in yet another template: given a variadic template my_list
, you could structure your my_convertible
to be called as
my_convertible<my_list<Args2...>, my_list<Args1...>>
The standard library already has a variadic template that works well here: tuple
. Not only that, but tuple<Args2...>
is convertible to tuple<Args1...>
if and only if Args2...
are convertible to Args1...
, so you can just write:
std::is_convertible<std::tuple<Args2...>, std::tuple<Args1...>>
Note: in the comments, @zatm8 reports that this doesn't always work: std::is_convertible<std::tuple<const char *&&>, std::tuple<std::string &&>>::value
is reported as false
, but std::is_convertible<const char *&&, std::string &&>::value
is reported as true
.
I believe that this is a bug, that they should both be reported as true
. The problem is reproducible on http://gcc.godbolt.org/ with clang 3.9.1. It is not reproducible with gcc 6.3, and it is also not reproducible with clang 3.9.1 when using -stdlib=libc++
. It appears that libstdc++ is using a language feature that clang doesn't quite handle correctly, and reducing it to a short example that does not rely 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 has been reported in 2014 as https://llvm.org/bugs/show_bug.cgi?id=19917.
It appears that this has been fixed in late 2016, but the fix has not yet made it into a released version: http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20161031/175955.html
If you are affected by this, you may wish to avoid std::tuple
and use @Yakk's answer instead.
回答2:
Yes.
First, how to do it. Then, why you shouldn't do it.
How to do it:
Write regroup, that takes a list of kN elements and groups it into N groups of k, interleaved. Groups can be template<class...>struct types{};
.
Then write apply, that takes a template<class...>class Z
and a class...
of groups (aka types<...>
), and applies Z
to the contents of each of the bundles, returning a types<...>
of the result.
Then fold over the contents of types<...>
using template<class A, class B> struct and_types:std::integral_constant<bool, A{}&&B{}>{};
.
I would find this mostly pointless, so I won't implement it. It should be easy with a decent metaprogramming library, most of the above operations are bog-standard.
Why you shouldn't
But really, given your example, just do this:
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>...>{}>
does the job. All of the shuffling is just noise.
With fold ...
support from C++1z, we get rid of and_types
as well and just use &&
and ...
.
回答3:
You can use std::conjuction to fold all result types 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 remember to check if
sizeof... (Args1) == sizeof... (Args2)
using if constexpr or enable_if
来源:https://stackoverflow.com/questions/34615570/variadic-version-of-stdis-convertible