部分专用模板作为模板参数

问题描述

比如说我有一个函数 foo() 带有一个参数包 Ts。然而,如果只有一种类型满足 Ts 的要求,该函数应该只接受参数包 std::is_integral。我编写的以下代码完全符合预期。

#include <type_traits>

template <template <typename> typename predicate,typename... Ts>
inline constexpr std::size_t count_if = ((predicate<Ts>::value ? 1 : 0) + ...);

// this method only compiles if exactly one of the given template arguments is of an integral type
template <typename... Ts>
void foo() requires (count_if<std::is_integral,Ts...> == 1) {}

int main()
{
    foo<int,double,float>(); //this compiles
    //foo<int,long,float>(); //this doesn't,as it should
}

但是,假设函数 foo() 被模板类型 T 的参数扩展,如下所示:

#include <type_traits>

template <template <typename> typename predicate,typename... Ts>
inline constexpr std::size_t count_if = ((predicate<Ts>::value ? 1 : 0) + ...);

// this method only compiles if one of the given template arguments is of an integral type
template <typename T,typename... Ts>
void foo(T) requires (count_if<std::is_integral,Ts...> == 1) {}

int main()

{
    foo<double,int,float>(double{}); //this compiles
    foo<int,float>(int{}); //this compiles
    //foo<double,float>(double{}); //this doesn't,as it should
}

现在我的问题是:是否有可能使我给我的 count_if 函数的谓词依赖于我的函数 T 的模板参数 foo()?>

例如,我可以检查以下内容

#include <type_traits>

template <template <typename> typename predicate,typename... Ts>
inline constexpr std::size_t count_if = ((predicate<Ts>::value ? 1 : 0) + ...);

template <typename T,typename... Ts>
void foo(T) requires (count_if<std::is_same<T,?????>,Ts...> == 1) {}

int main()
{
    foo<double,float>(double{}); //this should compile,as there is exactly one type in parameter pack of type double
    foo<int,float>(int{}); //this should compile,as there is exactly one type in parameter pack of type int
    //foo<int,float>(); //this shouldn't compile,as there is no int in parameter pack
    //foo<double,double>(); //this shouldn't compile,as there are two doubles in paramter pack
}

这有可能吗?我尝试在函数体内使用 static_assert() 检查它,但我无法在其中定义模板结构,我需要它以便我可以像这样编写自己的谓词,例如:

template <typename T,typename... Ts>
void foo(T) 
{
  template <typename U>
  struct custom_predicate
  {
    static constexpr bool value = std::is_same<T,U>::value;
  };
    
  static_assert(count_if<custom_predicate,Ts...> == 1);
}

但正如我所说,这是不可能的,因为我无法在函数体内定义模板结构。

有人有想法吗?

解决方法

在我看来你正在寻找

template <template <typename,typename> typename predicate,typename T,typename... Ts>
inline constexpr std::size_t count_if = ((predicate<T,Ts>::value ? 1 : 0) + ...);

template <typename T,typename... Ts>
void foo(T) requires (count_if<std::is_same,T,Ts...> == 1) {}

但是,这样一来,您就必须更改 count_if 要求。

如果您想要(如您在 static_assert() 示例中尝试过的)一种生成特定 is_same 的方法来修复第一种类型,您可以编写类似于 custom_predicate 但在函数之外的内容.

例如

template <template <typename> typename predicate,typename... Ts>
inline constexpr std::size_t count_if = ((predicate<Ts>::value ? 1 : 0) + ...);

template <typename T>
struct my_predicate
 {
   template <typename U>
   using my_is_same = std::is_same<T,U>;
 };

template <typename T,typename... Ts>
void foo(T) requires (count_if<my_predicate<T>::template my_is_same,Ts...> == 1)
 { /* ... */ }

或者,为了更灵活(并传递相同的 std::is_same 作为参数)

template <template <typename> typename predicate,typename... Ts>
inline constexpr std::size_t count_if = ((predicate<Ts>::value ? 1 : 0) + ...);

template <template <typename...> class C,typename ... Ts>
struct my_predicate
 {
   template <typename ... Us>
   using my_is_same = C<Ts...,Us...>;
 };

template <typename T,typename... Ts>
void foo(T)
   requires (count_if<my_predicate<std::is_same,T>::template my_is_same,Ts...> == 1)
 { }