创建可折叠模板参数包

Create foldable template parameter pack

问题

是否可以制作可折叠的(※fold expression) template 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(衰减的)参数的过滤函数)?

Is it possible to create template parameter pack that can be folded to avoid writing same fragment of code multiple times?

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

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


I've tried to solve the problem using T pack and aliasing single L and 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;
}

来不及玩了?

Is it possible to create template parameter pack that can be folded to avoid writing same fragment of code multiple times?

据我所知,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到enable/disableF()如下

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
}