【问题标题】:Variadic version of std::is_convertible?std::is_convertible 的可变版本?
【发布时间】:2016-04-09 12:10:15
【问题描述】:

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

【问题讨论】:

  • 如果有6个模板(T1, T2, T3, T4, T5, T6)怎么办?似乎最好将邻居分组:is_convertible&lt;T1, T2&gt; &amp;&amp; is_convertible&lt;T3, T4&gt; &amp;&amp; is_convertible&lt;T5, T6&gt;.
  • @erip 然后我需要以某种方式将两个参数包中的参数交错,这似乎同样困难。
  • 为什么需要交错?
  • @erip 我用一个例子更新了我的帖子。
  • @user697683 你真的不希望 thing&lt;A, B&gt;(c, d, e, f) 编译只是因为 (D, E, F) 可以转换 (A, B, C)。您不能假设您的 Args1...Args2... 的长度相同,因为您无法断言它们的长度相同。

标签: c++ variadic-templates


【解决方案1】:

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

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

标准库已经有一个在这里可以很好地工作的可变参数模板:tuple。不仅如此,当且仅当Args2... 可以转换为Args1... 时,tuple&lt;Args2...&gt; 可以转换为tuple&lt;Args1...&gt;,所以你可以这样写:

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

注意:在 cmets 中,@zatm8 报告这并不总是有效:std::is_convertible&lt;std::tuple&lt;const char *&amp;&amp;&gt;, std::tuple&lt;std::string &amp;&amp;&gt;&gt;::value 报告为 false,但 std::is_convertible&lt;const char *&amp;&amp;, std::string &amp;&amp;&gt;::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 的答案。

【讨论】:

  • 并非所有类型的元组都是有效的解决方案。示例: std::is_convertible_v<:tuple char>,std::tuple<:string>> 返回 false,但 std::is_convertible_v 返回 true
  • @zatm8 感谢您的评论。我相信这是一个编译器错误,请查看我的编辑。
【解决方案2】:

是的。

首先,怎么做。那么,为什么你不应该这样做。

怎么做:

编写 regroup,它采用 kN 个元素的列表并将其分组为 N 个 k 组,交错排列。群组可以是template&lt;class...&gt;struct types{};

然后编写 apply,它接受 template&lt;class...&gt;class Zclass... 组(又名 types&lt;...&gt;),并将 Z 应用于每个包的内容,返回结果的 types&lt;...&gt;

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

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


为什么你不应该

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

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

【讨论】:

  • 您能否详细说明如何编写重组或链接到一个实现示例?它可以满足我的好奇心:)
  • 您也可以检查相应的tuples 是否可分配,但我不确定解决方案是否比您建议的更简单。
  • @ysc 我能想到很多丑陋的方法。对于一个漂亮的方法,问一个用lisp(或haskell)编程的人,然后翻译成C++ TMP应该可以工作。我的意思是,get_every_nth 和 pop_front_then 和 bind 是一个,或者分成 k 个 N 组(不交错),然后旋转,是另一个。都乱七八糟的;因此找到具有漂亮列表原语的语言,并实现它们。
  • 我明白了。最后很简单。 template&lt;size_t N, T...&gt; struct at; 是“硬”部分,几行就可以了。
  • @YSC at 的“几行”版本具有 O(N) 实例化深度。一个高质量的at 在 O(lg(N)) 深度(仍然是 O(N) 实例,但递归深度受到使用树结构的限制),但需要多于几行。这很重要,因为低质量的at 生成的总类型名长度为 O(N^2)(因此内存/编译时间成本很高),并且编译器通常在模板实例化递归深度方面受到限制。 (先写高质量的“split”。split&lt;X,list...&gt;::left = concat&lt; split&lt;X/2, list... &gt;::left, split&lt;X-X/2, split&lt;X/2, list...&gt;::right&gt;::left &gt;伪代码。)
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 2021-01-13
    • 1970-01-01
    • 2020-10-19
    • 2017-12-11
    • 2011-11-24
    • 2018-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多