C ++, передавая функции структуры в функцию в качестве параметра - PullRequest
0 голосов
/ 28 декабря 2018

Прежде всего, я хотел бы сказать, что это первый вопрос, который я задаю для stackOverflow, поэтому я прошу прощения, если я не достаточно ясен.

Мой вопрос касается параметрического обращения к структуреособенность внутри функции. Я работаю в C ++.

Чего я действительно хочу добиться, так это уметь сортировать вектор объектов структуры (или объектов классов) на основе определенной функции структуры, который задается как параметр. Я также хочу указать тип структуры через шаблон, поэтому некоторые обходные пути, которые имеют дело с конкретными ситуациями, могут вообще не работать.

Я покажуупрощенный пример того, что я имею в виду.

Допустим, у меня есть структура под названием "человек" с функциями: "возраст", "рост", "вес".

Давайтетакже предположим, что у меня есть вектор «человеческих» объектов под названием «человечество».

Здесь, скажем, я хочу создать функцию, которая может выводить на экран возраст, рост или вес каждого элемента, в зависимости от того, чтоЯ прохожу какпараметр.

Код, приведенный ниже, явно не работает. Я прошу правильный способ сделать это.

struct human{
    int age;
    int height;
    int weight;
};

void show(vector<human> &elements, int value){
    for (int i=0; i<elements.size(); i++)
        cout << elements[i].value << endl;
}

int main{
    ...
    vector<human> mankind;
    ...
    show(mankind, age);
    show(mankind, height);
    show(mankind, weight);
    ...
    return 0;
}

Я хочу отметить, чтоЭтот пример очень простой случай.Конечно, я могу сделать эту работу, если я создаю отдельные функции для каждой функции или если я использую дерзкий способ, например, передавая строку "age", "height" или "weight" в качестве параметра, проверяя ее внутри функции и имеясовершенно отдельный случай для каждого из них.

Однако такие обходные пути не будут работать в общем случае проблемы, особенно если у меня много различных типов структур (пропущенных через template T и vector< T >) и особенности.

Ответы [ 3 ]

0 голосов
/ 28 декабря 2018

Один из способов сделать это - использовать указатели на члены , функцию C ++, которая позволяет создавать указатели, которые ссылаются на конкретные поля struct или class.

Синтаксис здесь может выглядеть немного страшно, но на самом деле он не так уж и плох.Например, вот как вы можете получить указатель на поле height вашей структуры human:

int human::* ptr = &human::height;

Здесь синтаксис int human::* ptr означает

  1. что это указатель на что-то внутри типа human, в частности
  2. , это указатель на элемент данных int
  3. , что элемент данных является полем height.

Когда у вас есть этот указатель, вы можете объединить его со структурой human, чтобы выделить именно этот элемент, как этот:

human agatha;
cout << agatha.*ptr << endl;

Здесь оператор .* означает «подтянуть поле, на которое указывает ptr.

В вашем случае вы могли бы собрать вещи, как это:

void show(vector<human> &elements, int human::* value){
    for (int i=0; i<elements.size(); i++)
        cout << elements[i].*value << endl;
}

int main{
    ...
    vector<human> mankind;
    ...
    show(mankind, &human::age);
    show(mankind, &human::height);
    show(mankind, &human::weight);
    ...
    return 0;
}
0 голосов
/ 29 декабря 2018

Вот некоторые соображения и улучшения для других ответов:

1) Если ваша структура содержит члены разных типов, вам придется перегружать все функции, используя указатель на член для каждого из типов,Или вам придется использовать шаблон, например:

#include <vector>
#include <iostream>

struct human{
    int age;
    int height;
    float weight;
};

template<typename T>
void show(std::vector<human> &elements,  T human::* value){
    for (int i=0; i<elements.size(); i++)
        std::cout << elements[i].*value << std::endl;
} 

2) Я считаю, что лямбда-подход более гибкий, поскольку он позволяет использовать комбинированные функции, которые могут оказаться полезными в реальных приложениях:

// body-mass-index
auto bmi = [](const human& e) { return e.height / (e.weight * e.weight); };

show(mankind, bmi);

3) Кроме того, лямбда-подход позволяет вам манипулировать функциями для сортировки, фильтрации и т. Д.:

auto inverse = [](auto func) {
    return [=](const human& e) { return -func(e);};
};

template<typename T, typename Func>
void sort(std::vector<T> &elements, Func f) {
    std::sort(elements.begin(), elements.end(), [=](auto const &lhs, auto const &rhs) { return f(lhs) < f(rhs); });
}

sort(mankind, inverse(bmi));

4) Лямбда-подход позволяет вам использовать именно указанный синтаксис вcall site, если вы помещаете лямбда-выражения в глобальные переменные (см. пример bmi выше).

5) Начиная с C ++ 14, у нас есть общие лямбда-выражения, поэтому вы можете повторно использовать лямбда-выражения для нескольких различных типов структур..

0 голосов
/ 28 декабря 2018

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

template <typename T, typename Func>
void show(vector<T> const& elements, Func f){
    for (auto const& e : elements)
        cout << f(e)<< endl;
}

, а затем вызвать ее как

show(mankind, [](auto const& e){return e.age;});
show(mankind, [](auto const& e){return e.height;});
show(mankind, [](auto const& e){return e.weight;});

Если show нужно показать этот элемент по порядку, тогда вы можетедаже использовать лямбду в вашем вызове std::sort, как

template <typename T, typename Func>
void show(vector<T>& elements, Func f){
    std::sort(elements.begin(), elements.end(), [=](auto const& lhs, auto const& rhs){return f(lhs) < f(rhs);});
    for (auto const& e : elements)
        cout << f(e)<< endl;
}

Таким образом, элемент для печати используется для вектора сортировки и для печати элемента.

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