函数指针的模板参数推导(g++ & ICC vs Clang++ & VC++)

template argument deduction for function pointer (g++ & ICC vs Clang++ & VC++ )

考虑以下程序:

#include <iostream>
template <typename T>
void foo(const T* x) {
    x();
}
void bar() { std::cout<<"bar() is called\n"; }
int main() {
    foo(bar);
}

它在 clang++VC++ 上编译得很好,但是 g++ 给出了以下编译器错误(参见现场演示 here

main.cpp: In function 'int main()':
main.cpp:10:9: error: no matching function for call to 'foo(void (&)())'
  foo(bar);
         ^
main.cpp:3:6: note: candidate: template<class T> void foo(const T*)
 void foo(const T* x) {
      ^~~
main.cpp:3:6: note:   template argument deduction/substitution failed:
main.cpp:10:9: note:   types 'const T' and 'void()' have incompatible cv-qualifiers
  foo(bar);
         ^

我在使用 g++ & clang++ 时使用了 -pedantic-errors 并且在使用 [=41= 时我使用了 /W4 & /Za 选项]++编译器。查看现场演示 here & here。所以,我想知道这里如何推导模板类型参数 T ?如果我从程序中删除 const 那么它在 g++ 上也可以正常编译。如果我使用 const T& 那么它在所有 3 个编译器上都能正常编译。那么,在这些情况下,这里将如何推导出类型?

更新:

此程序在 Intel C++ 编译器上也无法编译。请参阅现场演示 here。那么,这个错误是 g++ & Intel C++ 中的错误还是 Clang++ & VC++ 中的错误?

这本质上是CWG issue 1584:

It is not clear whether the following is well-formed or not:

void foo(){}
template<class T>   void deduce(const T*) { }

int main() {
  deduce(foo);   
}

Implementations vary in their treatment of this example.

目前仍然有效。真的不可能说哪个编译器是正确的。尽管如 2015 年的说明所示,CWG 目前的共识是应拒绝该提议。


为了提供更多上下文,我们必须记住带有 cv-qualifier-seq 的函数类型具有特殊含义(想想成员函数),而不仅仅是一种指定某些不可修改的类型。此外,您甚至不能以某种偷偷摸摸的方式添加 cv 资格,如 [dcl.fct]/7 所示:

The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored. [ Note: A function type that has a cv-qualifier-seq is not a cv-qualified type; there are no cv-qualified function types. — end note ][ Example:

typedef void F();
struct S {
  const F f;        // OK: equivalent to: void f();
};

— end example ]

该语言无法形成 const 限定函数类型。然而,我们需要的推导是将 const T 推导为 void()。前者一个const限定类型,而且还必须是一个函数类型。但那是不可能存在的类型!那么怎么推导出来呢?!

另一方面,如果您使用的是引用而不是指针,则标准中有机制可以推断出它。

所以目前还不清楚应该如何解决这个问题。一方面,今天的措辞不允许它 per-se,但另一方面,它的机制已经到位以供参考。所以一些实现继续对指针做同样的事情。