Возвратный тип возврата для оболочки функций-членов - PullRequest
0 голосов
/ 30 января 2019

У меня есть коллекция методов с разными сигнатурами, для которых всем нужен одинаковый префикс и постфиксный код, поэтому я бы хотел аккуратно завернуть каждый из них.Я пытаюсь сформулировать шаблон C ++ 14 variadic для универсальной оболочки для своих функций-членов, и у меня возникают проблемы при выводе возвращаемого типа.auto работает, но мне нужен явно тип возвращаемого значения, чтобы обеспечить допустимое возвращаемое значение, даже если я перехватываю исключение внутри оболочки при выполнении wrappee.

До сих пор мне удалось вывести тип возврата для простогофункция-обертка, использующая std::result_of, и я получил правильное поведение для нестатических функций-членов, использующих auto.Я два дня пытался заставить подход std::result_of работать для функций-членов, также пробовал множество вариантов decltype() и std::declval(), но пока безуспешно.У меня заканчиваются идеи о том, как определить тип возвращаемого значения.

Это мой рабочий пример

#include <iostream>

int foo(int a, int b) { return a + b; }
int bar(char a, char b, char c) { return a + b * c; }


template<typename Fn, typename... Args>
typename std::result_of<Fn&(Args... )>::type 
wrapFunc(Fn f, Args... args) {
  //Do some prefix ops
  typename std::result_of<Fn&(Args... )>::type ret = f(std::forward<Args>(args)...);
  //Do some suffix ops
  return ret;
}

class MemberWithAuto {
  private:
  public:
    MemberWithAuto() {};
    int foo(int i, int j) { return i + j;}

    template<typename Fn, typename... Args>
    auto wrapper(Fn f, Args... args) {
      //Do some prefix ops
      auto ret = (*this.*f)(std::forward<Args>(args)...);
      //Do some suffix ops
      return ret;
    } 
};



int main() {

  std::cout << "FuncWrapper for foo with 1 + 2 returns "         << wrapFunc<decltype(foo)>(foo, 1, 2)      << std::endl;
  std::cout << "FuncWrapper for bar with 'a' + 'b' * 1  returns "  << wrapFunc<decltype(bar)>(bar, 'a','b', 1)  << std::endl;

  MemberWithAuto meau = MemberWithAuto();
  std::cout << "MemberFunction with Auto with 6 + 1 returns  "  << meau.wrapper(&MemberWithAuto::foo, 6, 1)  << std::endl;

  return 0;
}

Оба они работают хорошо, но метод-обертка, использующий auto, не получаетмне тип возврата для последующего использования.Я перепробовал множество вариантов с std::result_of и decltype() со следующим кодом, но я не могу заставить его правильно скомпилировать

#include <iostream>

int foo(int a, int b) { return a + b; }
int bar(char a, char b, char c) { return a + b * c; }

class MemberWithDecl {
  private:
  public:
    MemberWithDecl() {};
    int foo(int i, int j) { return i + j;}

    template<typename Fn, typename... Args>
    typename std::result_of<Fn&(Args... )>::type wrapper(Fn f, Args... args) {
      //Do some prefix ops
      typename std::result_of<Fn&(Args... )>::type ret = (*this.*f)(std::forward<Args>(args)...);
      //Do some suffix ops
      return ret;
    } 
};



int main() {
  MemberWithDecl medcl = MemberWithDecl();
  std::cout << "MemberFunction with declaration also works "  << medcl.wrapper(&MemberWithDecl::foo, 6, 1)  << std::endl;
  return 0;
}

Я ожидал найти решение, где сигнатура Fn с Args... правильно распознается, потому что auto также успешно выводит типы.Кажется, что мое объявление типа не находит подходящий шаблон, независимо от того, какие варианты я пробовал, я получаю

error: no matching function for call to ‘MemberWithDecl::wrapper(int (MemberWithDecl::*)(int, int), int, int)’

Если я оставлю возвращаемый тип оболочки автоматически, и просто попробую мое объявление для переменной ret внутри, я получаю

error: no type named ‘type’ in ‘class std::result_of<int (MemberWithDecl::*&(int, int))(int, int)>’
       typename std::result_of<Fn&(Args... )>::type ret = (*this.*f)(std::forward<Args>(args)...);

После прочтения стандарта, я думаю, это означает, что result_of не считает Fn&(Args... ) правильной формой, но я не знаю, как должна выглядеть правильная формакак.

Любая помощь будет высоко ценится

1 Ответ

0 голосов
/ 30 января 2019

Вам не нужно std::result_of;Вы можете просто написать

  template <typename R, typename ... As1, typename ... As2>
  R wrapper (R(MemberWithDecl::*fn)(As1...), As2 ... args)
   { return (*this.*fn)(std::forward<As2>(args)...); } 

Если вы действительно хотите использовать std::result_of, вы должны добавить указатель MemberWithDecl (тип this) в качестве первого аргумента.

  template <typename Fn, typename ... Args>
  typename std::result_of<Fn&(MemberWithDecl*, Args... )>::type
        wrapper (Fn f, Args ... args)
   {
     typename std::result_of<Fn&(MemberWithDecl*, Args... )>::type ret
        = (*this.*f)(std::forward<Args>(args)...);

     return ret;
   } 

- РЕДАКТИРОВАТЬ -

Операционный запрос

но не могли бы вы уточнить, почему в вашем первом решении необходимы два отдельных набора параметров?

Два отдельных набора параметров строго не требуются, но это дает большую гибкость.

Предположим, у вас есть один набор

  template <typename R, typename ... As>
  R wrapper (R(MemberWithDecl::*fn)(As...), As ... args)
   { return (*this.*fn)(std::forward<As>(args)...); } 

и дано

 long foo (long a, long b) { return a + b; }

предположим, что вы вызываете

 wrapFunc(foo, 1, 2) 

Теперь компилятор должен вывести As... из foo() и из 1, 2.

Из foo(), компилятор выведетAs... as long, long.

Начиная с 1, 2, компилятор выводит As... как int, int.

Итак: ошибка компиляции, поскольку у компилятора есть конфликт дедукции.

При двойном наборе типов (As1... и As2...) компилятор выводит As1... как long, long и As2... как int, int.Нет конфликта, поэтому нет ошибки компиляции.

И нет также проблемы вызова foo() со значениями int, потому что int s преобразуются в long s.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...