【问题标题】:Create foldable template parameter pack创建可折叠模板参数包
【发布时间】:2018-12-19 18:52:28
【问题描述】:

问题

是否可以创建可折叠(※fold expression)模板parameter pack

示例

考虑以下示例(函数接受两个 int (decayed) 类型的参数)。

template<
    typename L,
    typename R,
    typename = std::enable_if_t<
            std::is_same_v<int, std::decay_t<L>>
        &&  std::is_same_v<int, std::decay_t<R>>
    >
>
int F(L Left, R Right){
    return 0x70D0;
}

是否可以创建可以折叠的模板参数包以避免多次编写相同的代码片段(ie std::is_same_v)?

下面用std::pack 表示的东西可以简化使用SFINAE 吗?

typename = std::enable_if_t<(... && std::is_same_v<int, std::decay_t<std::pack<L, R>>>)>

我尝试过的

我尝试使用T 包和别名单个LR 来解决问题。 但是对于某些原因,以下代码在 MSVC 15.9.4+28307.222 上编译并运行没有错误(第二个 F 函数调用的第二个参数,已衰减,不等于 int):

template<
    typename... T,
    typename L = std::tuple_element_t<0, std::tuple<T...>>,
    typename R = std::tuple_element_t<1, std::tuple<T...>>,
    typename = std::enable_if_t<(... && std::is_same_v<int, std::decay_t<T>>)>
>
int F(L Left, R Right){
    return 0x70D0;
}

int main(){
    F(3, 5);   // OK
    F(3, "5"); // OK, but should not compile
}

PS 另外,我是否遗漏了上述代码中的某些内容以使 SFINAE 正常工作(仅使用 int, int(衰减)参数的过滤函数)?

【问题讨论】:

  • 我投票决定将此问题作为题外话结束,因为显然 OP 不再想要它了?
  • @Hans,这不是 SO 的工作方式。如果问题是关于主题的,那么它属于 SO 社区。我们不会删除这样的好内容。
  • @TobySpeight 我不确定是回滚 OP 对问题的删除还是 VTC - 我下次会回滚

标签: c++ c++17 variadic-templates template-meta-programming sfinae


【解决方案1】:

是否可以创建可折叠的模板参数包以避免多次编写相同的代码片段?

到位了吗?不在 C++17 中。您必须将您的类型包装成某种template &lt;typename...&gt; struct typelist;,然后在其他地方解开它们。这需要一层间接。

据我所知,没有办法写出像std::pack 这样的东西。


我尝试使用 T 包和别名单个 L 和 R 来解决问题。[...]

在您的代码中,T... 将始终为空,因为它没有被任何东西推断出来。 LR 的默认模板参数值被忽略,因为它们是由函数调用推导出来的。

你需要这样的东西:

template<
    typename... T,
    typename = std::enable_if_t<(... && std::is_same_v<int, T>)>
>
int F(T...){
    return 0x70D0;
}

在 C++20 中,您应该能够使用 lambda,如下所示:

template<
    typename L,
    typename R,
    typename = std::enable_if_t<[]<typename... Ts>(){
        return (... && std::is_same_v<int, Ts>)
    }.operator()<L, R>()>
>
int F(L Left, R Right){
    return 0x70D0;
}

【讨论】:

  • 可以删除decay_t&lt;T&gt;,因为您按价值取值(decay_t&lt;T&gt; 将只是T
  • @Barry:已修复,还添加了可能的 C++20 解决方案。
  • 大声笑我很想删除我的赞成票,因为我认真地建议了一个 ;-)
  • std::tuple 可以像这个假设的std::pack 一样富有想象力地使用。
【解决方案2】:

来不及玩了?

是否可以创建可折叠的模板参数包以避免多次编写相同的代码片段?

据我所知,F() 本身并不存在。

但是你可以重新打包类型,例如,在一个被调用函数的列表中。

我的意思是...如果你 declare 定义 (仅声明:不需要定义它,因为仅在 decltype() 中使用) 以下函数 [编辑:正如 Barry 所建议的(谢谢),定义函数简化了使用]

template <typename T, typename ... Ts>
constexpr auto isSameList ()
   -> std::bool_constant<(... && std::is_same_v<T, std::decay_t<Ts>>)>
 { return {}; } 

在您可以使用模板折叠的地方,您可以通过 SFINAE 启用/禁用F(),如下所示

template <typename L, typename R,
          std::enable_if_t<isSameList<int, L, R>(), bool> = true>
int F(L Left, R Right)
 { return 0x70D0; }

以下是完整的编译示例

#include <type_traits>

template <typename T, typename ... Ts>
constexpr auto isSameList ()
   -> std::bool_constant<(... && std::is_same_v<T, std::decay_t<Ts>>)>
 { return {}; } 

template <typename L, typename R,
          std::enable_if_t<isSameList<int, L, R>(), bool> = true>
int F(L Left, R Right)
 { return 0x70D0; }

int main ()
 {
    F(3, 5);   // compile
    //F(3, "5"); // compilation error
 }

【讨论】:

  • 如果您定义 isSameList 来返回该对象,此模式会更好地工作,因此您可以将 decltype(f()){} 更改为 f()
  • @Barry - 你是对的:所以更简单优雅。谢谢!
【解决方案3】:

你差点就吃完了:

template <typename L,typename R,
  typename = std::enable_if_t<std::is_same_v<std::tuple<int,L,R>,std::tuple<L,R,int>>>>
int F(L Left, R Right){
    return 0x70D0;
}

int main(){
    F(3, 5);   // OK
    F(3, "5"); // Does not compile
    F("5", 3); // Does not compile
}

或可变参数版本:

template <typename... T,
  typename = std::enable_if_t<std::is_same_v<std::tuple<int,T...>,std::tuple<T...,int>>>>
int F(T... args){
    return 0x70D0;
}

int main(){
    F(3, 5);   // OK
    F(3, "5"); // Does not compile
    F("5", 3); // Does not compile
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-20
    • 1970-01-01
    • 1970-01-01
    • 2010-11-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多