Вернуть функцию из класса - PullRequest
6 голосов
/ 09 апреля 2019

Я хочу иметь возможность возвращать функцию из класса, чтобы мне не нужно было возвращать тип if-else.

У меня есть класс, который возвращает несколько строк. Вместо этого я хочу вернуть несколько функций.

#include <iostream>

class Handler
{
private:

public:
    int handleMessage(int code)
    {
        return code+1;
    }

};

void func1();
void func2();
void func3();

int main (int argc, char *argv[])
{
    Handler handle;
    int code = handle.handleMessage(0);
    if(code == 1)
    {
        func1();
    }
    return 0;
}

void func1(){ std::cout << "1" << std::endl;}
void func2(){ std::cout << "2" << std::endl;}
void func3(){ std::cout << "3" << std::endl;}

То, что я хочу: чтобы функция handleMessage в классе Handler возвращала что-то, чтобы в моем основном приложении мне не приходилось использовать if-else.

Итак, главное выглядит так:

function = handle.handleMessage(0); 

И приложение выберет, какую функцию оно будет запускать. например:

function = handle.handleMessage(0);  //will run func1 
function = handle.handleMessage(1);  //will run func2

Ответы [ 7 ]

6 голосов
/ 09 апреля 2019

Вы можете изменить функцию-член так, чтобы она возвращала указатель функции, например,

using fptr = void (*)();

struct Handler
{
    fptr handleMessage (int code)
    {
       if (code == 0)
          return &func1;
       else if (code == 1)
          return &func2;
       else
          return &func3;
    }

};

. Это может быть вызвано следующим образом

 Handler handle;
 auto f = handle.handleMessage(0);
 f();

Обратите внимание, что выше if -else if - else отправка не идеальна.Предпочитайте элемент данных, который хранит указатели на функции и связывает их с code, например, с помощью std::unordered_map.

. Обратите внимание, что когда вам потребуется возвращать объекты функций с состоянием в будущем, этот подход потерпит неудачу.Затем вам нужно охватить std::function, который может обернуть лямбды крышками или пользовательскими типами с перегрузкой operator().

6 голосов
/ 09 апреля 2019

Есть несколько способов сделать это, самый простой, вы можете использовать std::function.В этом примере мы возвращаем лямбда-функцию для каждого случая.Вы можете заменить его функциями, которые вы только что написали.

class Handler {
public:
    std::function<void()> handleMessage(int code) {
        code = code + 1; // ++code or whatever
        if (code == X) {
            return []() { std::cout << "Cool! I'am x!" << std::endl; };
        } else if (code == Y) {
            return []() { std::cout << "Cool! I'am x!" << std::endl; };
        } else if (...) {
            ...
        } else {
            ....
        }
    }
};

Тогда ваша основная функция становится:

int main (int argc, char *argv[]) {
    Handler handle;
    const auto func = handle.handleMessage(0);
    func();
    return 0;
}

Вы можете заменить оператор swith / if на массив, хранящий различные функции, как они упоминались в комментариях.

Если вы не хотите оплачивать дополнительный вызов виртуальной функции в отношении использования std::function, вы можете использовать псевдоним, такой как ответ ниже, или просто ключевое слово auto:

class Handler {
public:
    constexpr auto handleMessage(int code) {
        code = code + 1; // ++code or whatever
        if (code == X) {
            return &func1;
        } else if (code == Y) {
            return &func2;
        } else if (...) {
            ...
        } else {
            ....
        }
    }
};
1 голос
/ 09 апреля 2019

Использовать массив функций.

void func1(){  std::cout << "1" << std::endl; } 
void func2(){  std::cout << "2" << std::endl; } 
void func3(){  std::cout << "3" << std::endl; } 

typedef void (* func ) () ;

class Handler { 
  public:
      func handleMessage(int code)const{  
            static const func  F[] = { func1, func2, func3 };
            return F[ code ];
       }
};

int main() 
{
     Handler handler;
     func f = handler.handleMessage(0); // returns func1
     f();
}

живой пример

1 голос
/ 09 апреля 2019

Вы можете вернуть функцию с помощью return_type(*function_name)(argument_type1, argument_type2...), поэтому функция, которая выглядит следующим образом:

double f(int a, int b);

имеет имя double(*f)(int, int).

Стоит упомянуть C ++ 11 std::function, для которого требуется заголовок <functional>.Он имеет более интуитивное использование: std::function<double(int, int)>, но также добавляет немного накладных расходов.

Я также хотел бы предложить использовать C ++ 17's std::optional как для случая, когда переменная code выходит за пределы.Для этой реализации требуется заголовок <optional>. Использование

std::optional<void(*)()> handleMessage(int code){
    switch (code) {
    case 0: return std::optional(func1);
    case 1: return std::optional(func2);
    case 2: return std::optional(func3);
    }
    return std::nullopt; //empty
}

в основном выглядит следующим образом:

Handler handle;
auto func = handle.handleMessage(0);
if (func.has_value()) {
    func.value()();
}

, поскольку это позволяет проверить, если func.has_value()что довольно удобно.

1 голос
/ 09 апреля 2019

std::function это мощный инструмент.Крошечный брат - это простой указатель на функцию.Я преобразовал MCVE соответственно, чтобы вернуть указатель функции:

#include <iostream>

typedef void (*FuncPtr)();

void func1();
void func2();
void func3();
void funcError();

class Handler
{
private:

public:
    FuncPtr handleMessage(int code)
    {
      switch (code + 1) {
        case 1: return &func1;
        case 2: return &func2;
        case 3: return &func3;
        default: return &funcError;
      }
    }

};

int main (int argc, char *argv[])
{
    Handler handle;
    FuncPtr pFunc = handle.handleMessage(0);
    pFunc();
    return 0;
}

void func1(){ std::cout << "1" << std::endl;}
void func2(){ std::cout << "2" << std::endl;}
void func3(){ std::cout << "3" << std::endl;}
void funcError(){ std::cout << "ERROR!" << std::endl;}

Вывод:

1

Live Demo on coliru

0 голосов
/ 09 апреля 2019

Я хотел бы предложить решение без какого-либо блока if-else.Вам просто нужно шаблонизировать вашу функцию Handler::handleMessage.Примерно так:

// Class declaration
class Handler
{
private:

public:
  template<int code>
  void handleMessage();
};

и специализированный шаблон функции для конкретных кодов:

// Function template specializations.
template<>
void Handler::handleMessage<1>()
{
  std::cout << "1" << std::endl;
}
template<>
void Handler::handleMessage<2>()
{
  std::cout << "2" << std::endl;;
}
template<>
void Handler::handleMessage<3>()
{
  std::cout << "3" << std::endl;;
}

// All cases, except 1, 2 and 3
template<int code>
void Handler::handleMessage()
{
  std::cout << "Anything else" << std::endl;;
}

Использование может выглядеть так:

Handler h;
h.handleMessage<1>();   // Prints 1
h.handleMessage<2>();   // Prints 2
h.handleMessage<3>();   // Prints 3
h.handleMessage<323>(); // Prints 'Anything else'
0 голосов
/ 09 апреля 2019

вы можете сопоставить целые числа функции или лямбде, но читайте перед тем, что делает at () и что происходит, если ключ не найден !!

void function1()
{
    std::cout << "q1" << std::endl;
}
void function2()
{
    std::cout << "q2" << std::endl;
}

int main(int argc, char* argv[])
{
    std::map<int, std::function<void(void)>> map;
    map.insert(std::make_pair(1, function1));
    map.insert(std::make_pair(1, function2));
    map.at(1)();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...