std::is_convertable的可变版本

Variadic version of std::is_convertible?

本文关键字:版本 convertable is std      更新时间:2023-10-16

是否可以编写可变版本的std::is_convertible?例如,are_convertible<T1, T2, T3, T4>将返回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){}
}

您不需要连接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。问题可在上重现http://gcc.godbolt.org/伴随着3.9.1的叮当声。当使用-stdlib=libc++时,它不能用gcc 6.3再现,也不能用clang 3.9.1再现。libstdc++似乎使用了clang无法正确处理的语言功能,将其简化为一个不依赖于标准库头的简短示例可以得到:

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的答案。

是。

首先,如何做。然后,为什么你不应该做。

如何做到:

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

然后写apply,它获取组的template<class...>class Zclass...(也称为types<...>),并将Z应用于每个束的内容,返回结果的types<...>

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

我会觉得这几乎毫无意义,所以我不会实现它。有了一个像样的元编程库应该很容易,上面的大多数操作都是标准的。


为什么你不应该

但实际上,举个例子,就这么做吧:

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对折叠...的支持,我们也去掉了and_types,只使用了&&...

您可以使用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