variadic/non-variadic 模板之间的函数类型衰减不一致?

Inconsistency in function type decay between variadic/non-variadic templates?

给定一个非可变函数模板:

template<class T>
void f(void(t)(T));

还有一些简单的函数:

void f1(int);
void f2(char);

这个有效:

f(f1);

t的类型变为void (*)(int)

然而,可变参数对应物:

template<class... T>
void f(void(...t)(T));

// call
f(f1, f2);

不起作用。编译器(gcc 和 clang)抱怨不匹配的类型 void(T)void (*)(int)。参见 DEMO

请注意,如果明确添加 *,它会正常工作:

template<class... T>
void f(void(*...t)(T));

那么,为什么非可变参数可以衰减函数类型而可变参数不能?

因为你的语法不太对。

template<class... T>
void f(void(t)(T...));

void f1(int);

void foo()
{
    f(f1);
}

使用 gcc 6.1.1 测试。编译无误。

在非可变版本中,您使用模板参数作为传递函数参数的参数。

因此,对于可变参数版本,这是扩展参数包的地方。

这说明了吗?

template<class... T>
void f(void(*...t)(T)) {
  std::cout << "Ptr to functions" << std::endl;
}

template<class... T>
void g(void(&...t)(T)) {
  std::cout << "Ref to functions" << std::endl;
}

template <class... T>
void h(void(...t)(T)) {
  std::cout << "Mmmm... potential of confusion" << std::endl;
}

void s1(int) {

}

void s2(long) {

}


#include <type_traits>
int main() {
  // this compiles Ok. The compiler knows
  //    pointer to functions will be passed
  //    and can infer their correspondent T
  f(s1,s2);

  // this compiles Ok. The compiler knows that
  //   reference to functions will be passed
  //   and can infer their correspondent T
  g(s1,s2);

     h(s1,s2);
  // ^^^^^^^^^ this will cause a compilation error telling:
  //   template argument deduction/substitution failed:
  //   mismatched types ‘void(T)’ and ‘void (*)(int)’
  // The compiler can't decide how the function-type parameters are passed
  //  by ptr, by ref, one by ref other by ptr? As such
  //  cannot deduce their correspondent T in the argpack 


  // however, this will compile OK!!!
  // The s1/s2 correspondent T-es in argpack are clearly specified, 
  // not a problem.
  h<int,long>(s1,s2);
  return 0;
}

AFAICS,代码很好(也支持 VC++ and ICC compile it). After all, template argument deduction seems to work with function types just as it does with function pointer or reference types; [temp.deduct.type]/18:

A template-argument can be deduced from a function […] type.

[temp.deduct.call]/1:

For a function parameter pack that occurs at the end of the parameter-declaration-list, deduction is performed for each remaining argument of the call, taking the type P of the declarator-id of the function parameter pack as the corresponding function template parameter type. Each deduction deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack.

特别是后一段确认存在一些不一致,因为第二种情况下包 T 的(不成功)扣除减少到情况 1 中的(成功)扣除。

我的猜测是 Clang 和 GCC 会在声明时衰减函数模板的参数类型,但在参数是包扩展时拒绝这样做(然后无法推断)。当我们将示例调用更改为 f(f1) 时,Clang 的错误消息是

note: candidate template ignored: could not match 'void (T)' against 'void (*)(int)'

所以论证实际上在演绎之前就已经衰减了。