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

时间:2018-03-15 09:30:24

标签: c++ const language-lawyer function-pointers template-deduction

考虑以下计划:

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

它在clang++&amp; 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);
         ^

我在使用-pedantic-errors&amp;时使用了g++ clang++我使用了/W4&amp;使用VC ++编译器时的/Za选项。观看现场演示here&amp; here。那么,我想知道模板类型参数T将如何推导出来?如果我从程序中删除const,那么它也会在g++上编译正常。如果我使用const T&那么它在所有3个编译器上编译都很好。那么,在这些情况下,如何确切地推断出类型?

更新

此程序也无法在英特尔C ++编译器上进行编译。查看实时演示here。那么,这是g++&amp;英特尔C ++或Clang++&amp;中的错误VC ++?

1 个答案:

答案 0 :(得分:9)

这基本上是CWG issue 1584

  

目前尚不清楚以下是否格式良好:

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

int main() {
  deduce(foo);   
}
     

实施方案对此示例的处理方式各不相同。

目前仍处于活跃状态。它真的不可能说哪个编译器是正确的。虽然从2015年的说明中可以看出,CWG目前的共识是应该拒绝这一点。

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

  

cv-qualifier-seq在函数声明符中的效果不是   与在函数类型之上添加cv-qualification相同。在里面   后一种情况,cv限定符被忽略。 [注意:一种功能类型   具有cv-qualifier-seq的不是cv限定类型;没有   cv限定的函数类型。 - 尾注] [例子:

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

- 结束示例]

语言中没有办法形成const限定的函数类型。然而,我们需要的推论是将const T推导为void()。前者一个const限定类型,它也必须是一个函数类型。但那是一种不存在的类型!那怎么能推断出来呢?!

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

所以不清楚如何解决这个问题。一方面,今天的措辞本身并不允许,但另一方面,它的机制已经到位以供参考。因此,有些实现会继续执行并为指针执行相同操作。