【问题标题】:return-value deduction for non-static method pointers非静态方法指针的返回值推导
【发布时间】:2020-07-30 08:28:51
【问题描述】:

我正在玩弄指向非静态方法的函数指针,并偶然发现了一些对我来说不太有意义的 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”(编辑:它的签名)可以这么说。但是,这可以编译 (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&lt;int&gt;(&amp;T::print)

所以,问题是:为什么 GCC 可以推导出模板参数 Something,尽管它不是 print 方法签名的一部分,为什么当遇到派生自定义实际方法?

【问题讨论】:

    标签: c++ c++11 gcc template-argument-deduction


    【解决方案1】:

    看起来你有两个微妙的因素对你不利。首先,从int (X::*)()int (Y::*)() is implicit 的转换,所以经常可以不假思索地使用它。经常。但是您的错误消息中提到了“论据扣除/替代”。

    在模板参数推导期间,implicit conversions are not considered。因此,在这种情况下,您提供的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”可能是指“signature”?确实,(非模板)函数的签名不包括返回类型,但您处理的是类型,而不是签名。函数指针的类型确实包括其参数的类型和返回类型。

    【讨论】:

    • 哦,所以在确定要调用的函数时,&amp;T::printT=Y 会转换为 &amp;X::print,因为 Y 没有自己的 print 方法?这是有道理的。我猜with Something = Something 是因为编译器首先尝试(但失败)推断我指的是哪个函数,然后再删除模板参数?
    • @igel 我不确定,但也许是这样。无论如何,该消息很可能表明在检测到错误之前没有确定Something 的值。或者它应该被视为无论Something 解析为什么,都会出现错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-11
    • 1970-01-01
    相关资源
    最近更新 更多