std::is_convertible 的可变版本?

Variadic version of std::is_convertible?

是否可以编写 std::is_convertible 的可变版本?例如 are_convertible<T1, T2, T3, T4> 会 return is_convertible<T1, T3> && is_convertible<T2, T4>。我已经考虑了几个小时,但想不出任何合理的办法。

澄清一下,我想像这样使用它:

template <class ...Args1>
struct thing
{
  template <class ...Args2>
  enable_if_t<are_convertible<Args2..., Args1...>::value>
  foo(Args2 &&...args){}
}

是的。

首先,如何去做。那么,为什么你不应该这样做。

怎么做:

写regroup,它获取一个包含kN个元素的列表,并将其分成N个k组,交错排列。组可以是 template<class...>struct types{};.

然后编写应用,它采用 template<class...>class Zclass... 组(又名 types<...>),并将 Z 应用于每个包的内容, 返回结果的 types<...>

然后使用template<class A, class B> struct and_types:std::integral_constant<bool, A{}&&B{}>{};折叠types<...>的内容。

我会发现这几乎毫无意义,所以我不会实施它。有一个像样的元编程库应该很容易,上面的大部分操作都是bog-standard.


为什么你不应该

但实际上,以您的示例为例,只需这样做:

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...>{}>{};

然后:

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

完成任务。所有的洗牌都只是噪音。

有了 C++1z 的 fold ... 支持,我们也摆脱了 and_types,只使用 &&....

您不需要连接 Args2...Args1...,也不应该连接,因为这样做会让人无法分辨 Args2...Args1... 开始。以允许单独提取它们的方式传递多个可变参数的方法是将它们包装在另一个模板中:给定可变参数模板 my_list,您可以将 my_convertible 构造为

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

标准库已经有一个在这里运行良好的可变参数模板:tuple。不仅如此,当且仅当 Args2... 可转换为 Args1... 时,tuple<Args2...> 可转换为 tuple<Args1...>,因此您可以只写:

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

注意:在评论中,@zatm8 报告说这并不总是有效:std::is_convertible<std::tuple<const char *&&>, std::tuple<std::string &&>>::value 被报告为 false,但 std::is_convertible<const char *&&, std::string &&>::value 被报告为 true .

我认为这是一个错误,它们都应该报告为 true。使用 clang 3.9.1 可以在 http://gcc.godbolt.org/ 上重现该问题。它不能用 gcc 6.3 重现,并且在使用 -stdlib=libc++ 时也不能用 clang 3.9.1 重现。看起来 libstdc++ 正在使用 clang 无法正确处理的语言功能,并将其简化为不依赖于标准库的简短示例 headers 给出:

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

这被 gcc 接受,但被 clang 拒绝。它在 2014 年被报道为 https://llvm.org/bugs/show_bug.cgi?id=19917.

这似乎已在 2016 年底修复,但该修复尚未进入发布版本:http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20161031/175955.html

如果您受此影响,您可能希望避免std::tuple并改用@Yakk 的回答。

您可以使用 std::conjuction 将所有结果类型合并为一个:

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>...>;
};

记得检查是否

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

使用 if constexpr 或 enable_if