вычет возвращаемого значения для указателей метода не * stati c - PullRequest
2 голосов
/ 17 апреля 2020

Я возился с функциональными указателями на нестатические c методы и наткнулся на некоторые сообщения G CC, которые мне не совсем понятны. Давайте, возможно, рассмотрим некоторый код:

#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) и работает нормально.

Но , если я инстанцирую print_one с Y (раскомментировать последнюю строку в main()) вещи go юг:

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).

Итак, возникнут вопросы: почему G CC может вывести аргумент шаблона Something, хотя он не является частью сигнатуры метода print и почему вывод не выполняется, когда он сталкивается с классом, производным от класса, определяющего фактический метод?

1 Ответ

4 голосов
/ 17 апреля 2020

Похоже, у вас есть два тонких фактора, которые работают против вас. Во-первых, преобразование из int (X::*)() в int (Y::*)() неявно , поэтому часто его можно использовать, не задумываясь. Часто. Но в вашем сообщении об ошибке упоминается «вывод / замена аргумента».

Во время вывода аргумента шаблона неявные преобразования не считаются . Таким образом, в этом контексте предоставленный вами аргумент типа int (X::*)() не соответствует ожидаемому int (Y::*)(). (Является ли это несоответствие правильным или нет, я оставлю это другим. Оно существует в g ​​cc и 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

Я думал, что возвращаемое значение метода, так сказать, не влияет на его "идентификатор".

Под "идентификатором" вы, вероятно, подразумеваете " подпись "? Это правда, что сигнатура (не шаблонной) функции не включает тип возвращаемого значения, но вы имеете дело с типами, а не с сигнатурами. тип указателя функции включает в себя как типы его параметров, так и тип возвращаемого значения.

...