创建可折叠模板参数包

时间:2018-12-19 18:52:28

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

问题

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

示例

请考虑以下示例(带有两个类型为intdecayed)的参数的函数。

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)?

使用SFINAE可以简化下面std::pack的内容吗?

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(衰减)参数的函数)?

3 个答案:

答案 0 :(得分:3)

  

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

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

据我所知,无法写出std::pack之类的东西。


  

我试图使用T pack并为单个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;
}

答案 1 :(得分:1)

玩太晚了吗?

  

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

据我所知,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
 }

答案 2 :(得分:1)

您几乎拥有它:

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
}