C++:将结构体特征作为参数传递到函数中

5
首先,我想说这是我在stackOverflow上提出的第一个问题,如果表述不清楚,请见谅。
我的问题涉及在函数内部参数化地引用结构体特征。我使用C++语言。
我真正想要实现的是,能够根据给定的参数对一个包含结构体对象(或类对象)的向量进行排序。
我还希望通过模板来指定结构体的类型,因此一些处理特定情况的方法可能不适用于通用情况。
我将展示一个简单的例子,以说明我的意思。
假设我有一个名为“human”的结构体,其中包括“age”、“height”和“weight”等特征。
同时,假设我有一个名为“mankind”的“human”对象向量。
在这里,我想创建一个函数,根据我传递的参数,能够输出每个元素的年龄、身高或体重。
下面的代码显然行不通。我想请问正确的做法是什么。
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 Tvector< T >传递),以及许多功能。


关于排序:在这里 - jrok
但我必须说,在C#中使用LINQ操作非常容易 ;) - Alex Rivas
3个回答

4

有一种方法可以做到这一点,那就是使用指向成员的指针,这是C++的一个功能,它允许您创建指针,引用structclass的特定字段。

这里的语法可能看起来有点吓人,但实际上并不太难。例如,以下是如何获取指向human结构体的height字段的指针:

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

我同意,成员函数指针是C++在这里处理事情的方式。 - user3344003
谢谢!这正是我所需要的。运行得非常完美。 - New Guy

3

我更喜欢使用lambda方法解决这个问题。如果函数不知道将要打印什么,只有调用代码,则可以将一个包含要打印内容的lambda传递给函数。这样你就可以使函数完全通用,例如:

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

如果需要按顺序显示此成员,则可以在调用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;
}

因此,要打印的元素用于对向量进行排序并打印元素。


2
以下是对其他答案的想法和改进:
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) 我认为Lambda方法更加灵活,因为它允许您使用组合特性,在实际应用中可能非常有用:

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

show(mankind, bmi);

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) Lambda方法允许您在调用站点上使用完全指定的语法,如果将lambda放入全局变量中(请参见上面的bmi示例)。

5) 从C++14开始,我们拥有通用lambda,因此您可以将lambda重用于多种不同类型的结构体。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接