简体   繁体   English

C ++,将struct特性作为参数传递给函数

[英]C++, passing struct features into function as a parameter

First of all, I would like to say this is the first question I am asking on stackOverflow, so I apologize if I am not clear enough. 首先,我想说这是我在stackOverflow上提出的第一个问题,所以如果我不够清楚,我会道歉。

My question regards referring parametrically to a struct feature inside a function. 我的问题涉及参数化参考函数内的结构特征。 I work in C++. 我在C ++工作。

What I really want to achieve is to be able to sort a vector of struct objects (or class objects) based on a specific struct feature, which is given as a parameter. 我真正想要实现的是能够基于特定的结构特征对结构对象(或类对象)的向量进行排序,该结构特征作为参数给出。 I also want to give the type of struct through a template, so some workarounds that deal with specific situations might not work in general. 我还想通过模板给出结构类型,因此处理特定情况的一些解决方法可能不起作用。

I will show a simplistic example of what I mean. 我将展示一个简单的例子,说明我的意思。

Let's say, I have a struct called "human" with features: "age", "height", "weight". 比方说,我有一个名为“人”的结构,其特征是:“年龄”,“身高”,“体重”。

Let's also assume that I have a vector of "human" objects called "mankind". 我们还假设我有一个称为“人类”的“人”对象向量。

Here, let's say I want to make a function that can output to the screen each element's age, height or weight, depending on what I pass as a parameter. 在这里,假设我想创建一个函数,可以将每个元素的年龄,高度或重量输出到屏幕,具体取决于我作为参数传递的内容。

The code below obviously doesn't work. 下面的代码显然不起作用。 I am asking for the proper way to do this. 我要求正确的方法来做到这一点。

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;
}

I want to point out, that this example is a very simple case. 我想指出,这个例子是一个非常简单的例子。 Of course, I can make this work if I make separate functions for each feature or if I use a cheeky way, like passing a string "age" or "height" or "weight" as a parameter, checking it inside the function and having a completely separate case for each one. 当然,如果我为每个功能制作单独的功能,或者我使用一种厚颜无耻的方式,比如将字符串“age”或“height”或“weight”作为参数传递,在函数内部检查并具有每个人都有一个完全独立的案例。

However, such workarounds won't work in the general case of the problem, especially if I have many different types of structs (passed through a template T and vector< T > ) and features. 但是,这种解决方法在问题的一般情况下不起作用,特别是如果我有许多不同类型的结构(通过template Tvector< T > )和功能。

One way to do this is to use pointers-to-members , a C++ feature that lets you create pointers that refer to specific fields of a struct or class . 一种方法是使用指向成员的指针,这是一种C ++特性,允许您创建引用structclass特定字段的指针。

The syntax here might look a little scary, but it's actually not so bad. 这里的语法可能看起来有点可怕,但实际上并没有那么糟糕。 For example, here's how you'd get a pointer to the height field of your human struct: 例如,以下是如何获得指向human结构的height字段的指针:

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

Here, the syntax int human::* ptr means 这里,语法int human::* ptr表示

  1. that it's a pointer to something inside the human type; 它是指向human内部某事物的指针;
  2. specifically, it's a pointer to an int data member; 具体来说,它是一个指向int数据成员的指针; and
  3. that data member is the height field. 该数据成员是height字段。

Once you have this pointer, you can combine it with a human struct to isolate out just that member like this: 一旦你有了这个指针,就可以将它与一个human结构组合起来,以便像这样隔离出那个成员:

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

Here, the .* operator means "pull up the field pointed at by ptr . 这里, .*运算符意味着“拉起ptr指向的字段。

In your case, you might put things together like this: 在你的情况下,你可以像这样把事情放在一起:

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;
}

I prefer the lambda approach to this problem. 我更喜欢lambda方法来解决这个问题。 If the function doesn't know what will be printed, just the calling code, then you can pass a lambda to the function that contains the code of what to print. 如果函数不知道将要打印什么,只是调用代码,那么您可以将lambda传递给包含要打印内容的代码的函数。 That way you can make the function completely generic like 这样你就可以使函数完全通用了

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

and then you would call it like 然后你会称之为

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

If show needs to show this member in order then you can even leverage the lambda in you call to std::sort like 如果show需要按顺序显示这个成员,那么你甚至可以在调用std::sort类的时候利用lambda

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;
}

So the element to print is used to the sort vector and to print the element. 因此要打印的元素用于排序向量并打印元素。

Here are some thoughts on and improvements to the other answers: 以下是对其他答案的一些想法和改进:

1) If your struct contains members of different types, you'll have to overload all functions using the pointer to member for each of the types. 1)如果你的struct包含不同类型的成员,你必须使用指向每个类型的成员的指针来重载所有函数。 Or you'd have to use a template like: 或者您必须使用以下模板:

#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) I find the lambda approach more flexible, as it allows you to use combined features which could prove useful in real-world applications: 2)我发现lambda方法更灵活,因为它允许您使用在实际应用中可能有用的组合功能:

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

show(mankind, bmi);

3) Also, the lambda approach allows you to manipulate features for sorting, filtering etc: 3)此外,lambda方法允许您操作排序,过滤等功能:

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) The lambda approach allows you to use you exactly the specified syntax on the call site, if you put the lambdas into global variables (see the bmi example above). 4)如果将lambdas放入全局变量,lambda方法允许您在调用站点上使用完全指定的语法(请参阅上面的bmi示例)。

5) Starting with C++14 we have generic lambdas, so you can reuse the lambdas for multiple different types of structs. 5)从C ++ 14开始,我们有通用的lambdas,因此你可以将lambdas重用于多种不同类型的结构。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM