非静态方法指针的返回值推导



我在摆弄指向非静态方法的函数指针时,偶然发现了一些对我来说没有意义的GCC消息

#include<iostream>
struct X{  int print() const { std::cout << "hello"; return 0; } };
struct Y: public X{};
template<class T>
struct print_one
{
const T& t;
print_one(const T& _t): t(_t) {}
template<class Something>
void _call(Something (T::*fct)() const) const
{
std::cout << (t.*fct)() << 'n';
}
void do_print() const { _call(&T::print); }
};

int main()
{ 
X x;
Y y;
print_one<X>(x).do_print();
//print_one<Y>(y).do_print();
}

我已经准备好看到这个失败了,因为我认为一个方法的返回值对它的"ID"(EDIT:它的签名(没有贡献。但是,这会编译(gcc-9 --std=c++17(并运行良好。

但是,如果我用Y初始化print_one(取消对main()中最后一行的注释(,事情就会恶化:

test_ptr.cpp: In instantiation of 'void print_one<T>::do_print() const [with T = Y]':
test_ptr.cpp:28:28:   required from here
test_ptr.cpp:19:27: error: no matching function for call to 'print_one<Y>::_call(int (X::*)() const) const'
19 |   void do_print() const { _call(&T::print); }
|                           ^~~~~
test_ptr.cpp:14:8: note: candidate: 'template<class Something> void print_one<T>::_call(Something (T::*)() const) const [with Something = Something; T = Y]'
14 |   void _call(Something (T::*fct)() const) const
|        ^~~~~
test_ptr.cpp:14:8: note:   template argument deduction/substitution failed:
test_ptr.cpp:19:27: note:   mismatched types 'const Y' and 'const X'
19 |   void do_print() const { _call(&T::print); }
|                           ^~~~~

特别是with Something = Something对我来说似乎很奇怪。此外,如果我明确地给出这样的模板名称:_call<int>(&T::print),整个事情就可以了。

因此,问题是:为什么GCC可以推导模板参数Something,尽管它不是print方法签名的一部分,以及当遇到从定义实际方法的类派生的类时,为什么推导失败?

看起来有两个微妙的因素对您不利。首先,从int (X::*)()int (Y::*)()的转换是隐式的,所以它通常可以毫不犹豫地使用。经常但您的错误消息中提到了"参数推导/替换"。

在模板参数推导过程中,不考虑隐式转换。因此,在此上下文中,您提供的类型为int (X::*)()的参数与预期的int (Y::*)()不匹配(这种不匹配是否正确,我将留给其他人。它存在于gcc和clang中。(如果您要显式提供模板参数,则不需要进行参数推导,并且您将跳过执行隐式转换的步骤。

提供模板参数的简单方法不适合您的上下文,但它确实提供了概念证明。

_call<int>(&T::print); // Specify that the return type must be `int`

通过指定int,不需要推导模板参数。然而,这在很大程度上违背了模板的要点,从而限制了您的选择。幸运的是,我们可以做得更好。我们可以告诉编译器如何"推导"模板参数。(我使用了引号,因为这不是官方的模板推导步骤。(

_call<decltype(t.print())>(&T::print); // The desired generic return type

可以说,我认为方法的返回值对其"ID"没有贡献。

您所说的"ID"可能是指"签名"?的确,(非模板(函数的签名不包括返回类型,但您处理的是类型,而不是签名。函数指针的类型既包括其参数的类型,也包括其返回类型。

最新更新