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

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

本文关键字:ICC g++ vs Clang++ VC++ 指针 参数 函数      更新时间:2023-10-16

请考虑以下程序:

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

它在clang++VC++上编译良好,但g++给出了以下编译器错误(在此处查看现场演示)

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,在使用VC++编译器时使用了/W4&/Za选项。在此处和此处查看现场演示。那么,我想知道模板类型参数T将如何在这里推导?如果我从程序中删除const,那么它也可以在g++上很好地编译。如果我使用const T&那么它可以在所有 3 个编译器上编译良好。那么,在这些情况下,这里将如何准确推断类型?

更新:

该程序在英特尔C++编译器上编译时也失败。在此处观看现场演示。那么,这个错误是g++和英特尔C++中的错误还是Clang++和VC++中的错误?

这本质上是 CWG 问题 1584:

目前尚不清楚以下内容的格式是否正确:

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

实现在处理此示例时有所不同。

目前仍处于活动状态。实际上不可能说哪个编译器是正确的。尽管正如 2015 年的说明所示,CWG 目前的共识是应该拒绝这样做。


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

函数声明符中 cv-qualifier-seq 的效果不是 与在函数类型之上添加 CV 资格相同。在 后一种情况,将忽略 CV 限定符。[ 注意:函数类型 具有 CV 限定符序列的不是符合 CV 条件的类型;没有 符合 CV 标准的函数类型。— 尾注 ][ 示例:

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

— 结束示例 ]

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

另一方面,如果您使用参考而不是指针,标准中有机器可以推断它。

因此,目前尚不清楚应该如何解决这个问题。一方面,今天的措辞本身不允许这样做,但另一方面,它的机制已经到位,可供参考。因此,一些实现继续进行并对指针执行相同的操作。