函数指针的模板参数推导(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,但另一方面,它的机制已经到位以供参考。所以一些实现继续对指针做同样的事情。
考虑以下程序:
#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,但另一方面,它的机制已经到位以供参考。所以一些实现继续对指针做同样的事情。