0

换句话说:如何将自定义类中的各种字段传递给单个函数?

现在详细说明:我有一个std::vector包含一个类,例如CustomClass,我必须通过一些标准从该类的字段中提取结果,这些标准是该类中的字段,并以某种方式组合这些数据。

我解决这个问题的第一种方法是使用一个函数,它接受std::vector类的参数作为参数,以便提取数据并返回一个std:map. 此映射中的键是应组合数据的标准的类型,值是int来自该向量的所有成员的组合数据。

问题是标准不仅仅是一个 - 此类中的多个字段可以用作标准(为方便起见,所有标准都是std::string,如果不是 - 我可以使函数模板化)。

现在对我来说最简单的方法是用几乎相同的代码制作几十个函数,每个函数都从这个类中提取一个简单的具体字段。但是,更改可能需要对所有数十个功能进行类似更改,这将是一个令人头疼的维护问题。但是在这个阶段,我想不出如何将这个类中的一个字段传递给一个函数......

这是该类的示例代码:

// this is the class with data and criteria
class CustomClass
{
public:
    std::string criteria1;
    std::string criteria2;
    std::string criteria3;
//... and others criteria
    int dataToBeCombined;
// other code
};

// this is one of these functions
std::map<std::string, int> getDataByCriteria1(std::vector<CustomClass> aVector)
{
    std::map<std::string, int> result;
    foreach(CustomClass anObject in aVector)
    {
        if(result.find(anObject.criteria1)==result.end()) // if such of key doesn't exists
        {
            result.insert(std::make_pair(anObject.criteria1, anObject.dataToBeCombined));
        }
        else
        {
            // do some other stuff in order to combine data
        }
    }
    return result;
}

并且通过类似的方式,我应该制作应该与CustomClass::criteria2,CustomClass::criteria3等一起使用的其他功能。

我想将这些标准放在一个数组中,并仅将标准的数量传递给该函数,但该类将被其他人用于其他目的,并且字段必须易于阅读,因此这不是一个选项(即真实姓名不是criteria1criteria2等,而是描述性的)。

有想法的人吗?

编辑:有人将我的问题提到“具有不同返回类型的 C++ 相同函数参数”,这显然是非常不同的——在我的例子中,函数每次都返回相同的类型,只是它所采用的参数必须是一个类的各个字段。

4

5 回答 5

1

您可以使用指向成员的指针。在你的函数中声明一个参数std::string CustomClass::*pField,用 传递它,用&CustomClass::criteriaN访问它anObject.*pField

请参阅有关该主题的更多信息:指向数据成员的指针。

于 2016-07-11T12:19:30.960 回答
1

如果所有“标准”都属于同一类型,我看不到一个优雅的解决方案,但您可以以某种方式“枚举”它们并使用它们的编号。

例如,您可以通过这种方式声明模板化getVal()方法CustomClass

template <int I>
   const std::string & getVal () const;

并以这种方式(在课程主体之外)按数量、按标准实施它们

template <>
const std::string & CustomClass::getVal<1> () const
 { return criteria1; }

template <>
const std::string & CustomClass::getVal<2> () const
 { return criteria2; }

template <>
const std::string & CustomClass::getVal<3> () const
 { return criteria3; }

现在,您可以通过这种方式getDataByCriteria1()在模板化函数中进行转换getDataByCriteria()

template <int I>
std::map<std::string, int> getDataByCriteria (std::vector<CustomClass> aVector)
 {
   std::map<std::string, int> result;

   for (const auto & cc : aVector)
    {
      if ( result.find(cc.getVal<I>()) == result.end()) // if such of key doesn't exists
       {
         result.insert(std::make_pair(cc.getVal<I>(), cc.dataToBeCombined));
       }
      else
       {
            // do some other stuff in order to combine data
       }
    }

   return result;
 }

并以这种方式调用它

auto map1 = getDataByCriteria<1>(ccVec);
auto map2 = getDataByCriteria<2>(ccVec);
auto map3 = getDataByCriteria<3>(ccVec);

--- 编辑:为不同类型标准添加了解决方案(仅限 C++14)---

如果“标准”属于不同类型,则略有不同。

由于autodecltype().

例如,如果

std::string criteria1;
int         criteria2;
long        criteria3;

你可以getVal()声明auto

template <int I>
   const auto & getVal () const;

并定义(用auto)所有版本的getVal()

template <>
const auto & CustomClass::getVal<1> () const
 { return criteria1; }

template <>
const auto & CustomClass::getVal<2> () const
 { return criteria2; }

template <>
const auto & CustomClass::getVal<3> () const
 { return criteria3; }

并结合autodecltype()您可以通过getDataByCriteria()这种方式进行修改

template <int I>
auto getDataByCriteria (std::vector<CustomClass> aVector)
 {
   std::map<decltype(aVector[0].getVal<I>()), int> result;

   for (const auto & cc : aVector)
    {
      if ( result.find(cc.getVal<I>()) == result.end()) // if such of key doesn't exists
       {
         result.insert(std::make_pair(cc.getVal<I>(), cc.dataToBeCombined));
       }
      else
       {
            // do some other stuff in order to combine data
       }
    }

   return result;
 }

功能的使用保持不变(再次感谢auto

auto map1 = getDataByCriteria<1>(ccVec);
auto map2 = getDataByCriteria<2>(ccVec);
auto map3 = getDataByCriteria<3>(ccVec);

ps:注意:代码未经测试

ps2:对不起我的英语不好

于 2016-07-11T12:44:52.500 回答
0

您可以使用函数来提取文件,例如

std::string extractFiled(const CustomClass &object, int which) {
  switch (which) {
    case 1:
      return object.criteria1;
    case 2:
      return object.criteria2;
    case 3:
      return object.criteria3;
    default:
      return object.criteria1;
  }
}

getDataByCriteria添加一个 arg 以指示要使用的文件。或者你可以只使用宏来实现getDataByCriteria.

于 2016-07-11T12:43:53.010 回答
0

您没有指定在满足条件的各种条件下要执行的实际操作,因此很难说它们实际上可以组合多少。

这是使用std::accumulate()STL 以及一些附加功能的可能解决方案。此示例使用 Visual Studio 2015 编译。

如果大多数功能可以组合成一个相当小的累积函数,这种方法将是有意义的,因为大多数标准都以相同的方式处理。或者,您可以accumulate_op()在处理一般情况本身时让该函数针对特定情况调用其他函数。

您可以以此为起点并进行适当的修改。

一种这样的修改可能是摆脱使用std::map来维护状态。由于使用这种方法,您将根据标准迭代进行std::vector累积,我不确定std::map如果您正在累积,您是否甚至需要使用来记住任何内容。

// map_fold.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <numeric>

// this is the class with data and criteria
class CustomClass
{
public:
    CustomClass() : dataToBeCombined(0) {}
    std::string criteria1;
    std::string criteria2;
    std::string criteria3;
    //... and others criteria
    int dataToBeCombined;
    // other code
};

// This is the class that will contain the results as we accumulate across the
// vector of CustomClass items.
class Criteria_Result {
public:
    Criteria_Result() : dataToBeCombined(0) {}
    CustomClass myCriteria;
    std::map<std::string, int> result1;
    std::map<std::string, int> result2;
    std::map<std::string, int> result3;

    int dataToBeCombined;
};

// This is the accumulation function we provide to std::accumulate().
// This function will build our results.
class accumulate_op {
public:
    Criteria_Result * operator ()(Criteria_Result * x, CustomClass &item);

};

Criteria_Result * accumulate_op::operator ()(Criteria_Result *result, CustomClass &item)
{

    if (!result->myCriteria.criteria1.empty() && !item.criteria1.empty()) {
        std::map<std::string, int>::iterator it1 = result->result1.find(item.criteria1);
        if (it1 == result->result1.end()) // if such of key doesn't exists
        {
            result->result1.insert(std::make_pair(item.criteria1, item.dataToBeCombined));
        }
        else
        {
            // do some other stuff in order to combine data
            it1->second += item.dataToBeCombined;
        }
        result->dataToBeCombined += item.dataToBeCombined;
    }

    if (!result->myCriteria.criteria2.empty() && !item.criteria2.empty()) {
        std::map<std::string, int>::iterator it2 = result->result2.find(item.criteria2);
        if (it2 == result->result2.end()) // if such of key doesn't exists
        {
            result->result2.insert(std::make_pair(item.criteria2, item.dataToBeCombined));
        }
        else
        {
            // do some other stuff in order to combine data
            it2->second += item.dataToBeCombined;
        }
        result->dataToBeCombined += item.dataToBeCombined;
    }

    if (!result->myCriteria.criteria3.empty() && !item.criteria3.empty()) {
        std::map<std::string, int>::iterator it3 = result->result3.find(item.criteria3);
        if (it3 == result->result3.end()) // if such of key doesn't exists
        {
            result->result3.insert(std::make_pair(item.criteria3, item.dataToBeCombined));
        }
        else
        {
            // do some other stuff in order to combine data
            it3->second += item.dataToBeCombined;
        }
        result->dataToBeCombined += item.dataToBeCombined;
    }

    return result;
}

int main()
{
    Criteria_Result  result;
    std::vector<CustomClass> aVector;

    // set up the criteria for the search
    result.myCriteria.criteria1 = "string1";
    result.myCriteria.criteria2 = "string2";

    for (int i = 0; i < 10; i++) {
        CustomClass xx;

        xx.dataToBeCombined = i;
        if (i % 2) {
            xx.criteria1 = "string";
        }
        else {
            xx.criteria1 = "string1";
        }
        if (i % 3) {
            xx.criteria2 = "string";
        }
        else {
            xx.criteria2 = "string2";
        }

        aVector.push_back (xx);
    }

    // fold the vector into our results.
    std::accumulate (aVector.begin(), aVector.end(), &result, accumulate_op());

    std::cout << "Total Data to be combined " << result.dataToBeCombined << std::endl;

    std::cout << " result1 list " << std::endl;
    for (auto jj : result.result1) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }
    std::cout << " result2 list " << std::endl;
    for (auto jj : result.result2) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }
    std::cout << " result3 list " << std::endl;
    for (auto jj : result.result3) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }

    std::cout << " Trial two \n\n" << std::endl;

    result.myCriteria.criteria2 = "";
    result.result1.clear();
    result.result2.clear();
    result.result3.clear();
    result.dataToBeCombined = 0;

    // fold the vector into our results.
    std::accumulate(aVector.begin(), aVector.end(), &result, accumulate_op());

    std::cout << "Total Data to be combined " << result.dataToBeCombined << std::endl;

    std::cout << " result1 list " << std::endl;
    for (auto jj : result.result1) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }
    std::cout << " result2 list " << std::endl;
    for (auto jj : result.result2) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }
    std::cout << " result3 list " << std::endl;
    for (auto jj : result.result3) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }

    return 0;
}

这会产生如下输出:

Total Data to be combined 90
 result1 list
    string   25
    string1   20
 result2 list
    string   27
    string2   18
 result3 list
 Trial two


Total Data to be combined 45
 result1 list
    string   25
    string1   20
 result2 list
 result3 list
于 2016-07-11T20:20:55.930 回答
0

您将其标记为 C++11,因此请使用可变参数模板。

            class VariadicTest
            {
            public:
                VariadicTest()
                {
                    std::map<std::string, int> test1 = getDataByCriteria(testValues, criteria1);
                    std::map<std::string, int> test2 = getDataByCriteria(testValues, criteria2);
                    std::map<std::string, int> test3 = getDataByCriteria(testValues, criteria1, criteria2);
                    std::map<std::string, int> test4 = getDataByCriteria(testValues, criteria1, criteria3);
                }

            private:
                std::string criteria1 = { "Hello" };
                std::string criteria2 = { "world" };
                std::string criteria3 = { "." };

                std::vector<CustomClass> testValues = { {"Hello",1}, {"world",2},{ "!",3 } };

                template<typename T> std::map<std::string, int> getDataByCriteria(std::vector<CustomClass> values, T criteria)
                {
                    std::map<std::string, int> result;
                    //do whatever is needed here to filter values
                    for (auto v : values)
                    {
                        if (v.identifier == criteria)
                        {
                            result[values[0].identifier] = values[0].value;
                        }
                    }
                    return result;
                }

                template<typename T, typename... Args> std::map<std::string, int> getDataByCriteria(std::vector<CustomClass> values, T firstCriteria, Args... args)
                {
                    std::map<std::string, int> result = getDataByCriteria(values, firstCriteria);
                    std::map<std::string, int> trailer = getDataByCriteria(values, args...);
                    result.insert(trailer.begin(), trailer.end());
                    return result;
                }
            };
于 2016-07-11T12:22:38.763 回答