创建可折叠模板参数包

Create foldable template parameter pack

本文关键字:参数 可折叠 创建      更新时间:2023-10-16

问题

是否可以创建可折叠(※折叠表达式)模板参数包?

考虑以下示例(采用两个类型为int(衰减)的参数的函数)。

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

是否可以创建可以折叠的模板参数包,以避免多次编写相同的代码片段(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(衰减)参数的过滤器函数)?

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

到位?不是在第 17 C++。您必须将类型包装到某种template <typename...> 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;
}

玩太晚了吗?

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

据我所知,不是F()本身。

但是,您可以通过示例将这些类型重新打包到被调用函数的列表中。

我的意思是。。。如果你声明定义(仅声明:不需要定义它,因为仅在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
}

你几乎拥有它:

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
}