return-非静态方法指针的减值

return-value deduction for non-static method pointers

我正在研究指向非静态方法的函数指针,偶然发现了一些对我来说不太有意义的 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();
}

我已经准备好看到这个失败了,因为我认为一个方法的 return 值不会影响它的 "ID" (编辑:它的签名)可以这么说。但是,这可以编译 (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 方法签名的一部分,以及为什么在遇到 [= =34=] 派生自 class 定义实际方法?

看来您有两个微妙的因素在对您不利。首先,从 int (X::*)()int (Y::*)() is implicit 的转换,所以经常可以不假思索地使用它。经常。但是您的错误消息提到 "argument deduction/substitution".

在模板参数推导期间,implicit conversions are not considered。因此,在这种情况下,您提供的类型 int (X::*)() 的参数与预期的 int (Y::*)() 不匹配。 (这种不匹配是否正确是我留给其他人的事情。它存在于 gcc 和 clang 中。) 如果您要显式提供模板参数,则不需要参数推导,您将跳到执行隐式转换的步骤。

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

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

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

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

I thought the return value of a method does not contribute to its "ID" so to say.

"ID",您可能是指“signature”?确实,(非模板)函数的签名不包括 return 类型,但您处理的是类型,而不是签名。函数指针的 type 确实包括其参数的类型及其 return 类型。