可变版本的std :: is_convertible?

时间:2016-01-05 15:41:47

标签: c++ variadic-templates

是否可以编写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){}
}

3 个答案:

答案 0 :(得分:12)

您不需要连接Args2...Args1...,而且您不应该这样做,因为这样做会导致无法确定Args2...结束的位置和Args1...开始了。以允许单独提取多个可变参数的方式传递多个可变参数的方法是将它们包装在另一个模板中:给定一个可变参数模板my_list,您可以将my_convertible构造为被调用为

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

标准库已经有一个在这里运行良好的可变参数模板:tuple。不仅如此,tuple<Args2...>可转换为tuple<Args1...>当且仅当Args2...可转换为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不能正确处理,并将其简化为一个不依赖于标准库头的简短示例:

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

答案 1 :(得分:7)

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

怎么做:

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

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

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

我会发现这大多没有意义,所以我不会实现它。使用一个像样的元编程库应该很容易,上面的大多数操作都是标准的。

为什么你不应该

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

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,只使用&&...

答案 2 :(得分:0)

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