2016-07-11 11 views
0

В некоторых словах: как передать различные поля из пользовательского класса в одну функцию?Как передать поля из класса функции в C++?

Теперь в деталях: У меня есть 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 и т. д.

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

Кто-нибудь имеет идеи?

EDIT: Кто-то передал мой вопрос «C++ с теми же параметрами функции с другим типом возврата», который, очевидно, очень отличается - функция в моем случае возвращает каждый раз каждый раз, только требуемые параметры должны быть разными полями из класс.

+9

Этот вопрос полон слова, которые являются допустимыми словами на английском языке, но в сочетании таким образом, что не имеет смысла. «У меня есть std :: vector из класса». «этот вектор проходит через». «в качестве критериев можно использовать более одного поля этого класса». Этот вопрос нужно переписать так, чтобы он был более ясным и, вероятно, половиной его текущего размера. –

+0

«Вот пример кода из этого класса:« явно не потому, что 'foreach (CustomClass anObject в aVector)' не является C++. Кроме того, извините, но этот пост - салат из слова. Пожалуйста, объясните, шаг за шагом, используя короткие параграфы/маркеры, то, что вы пытаетесь сделать - желательно со специфическими ссылками на части вашего кода, которые должны быть фактически C++, должны присутствовать полностью и должны показывать пример ввода и желаемых выходных данных. –

+0

Возможный дубликат [C++ одинаковых параметров функции с другим типом возврата] (http://stackoverflow.com/questions/14840173/c-same-function-parameters-with-different-return-type) –

ответ

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.

+0

Он хочет вернуть другой тип 'map' на основе значения' criteria1'. Это всегда возвращает одно и то же значение. –

+0

@JonathanMee. В соответствии с тем, что я собираюсь сделать эти критерии в одном массиве и передать этой функции только количество критериев, которые, как сказал опросчик, все записи имеют одинаковый тип. В любом случае, ответ max66 лучше. – phyxnj

+0

Сразу после цитаты он говорит: «Так что это не вариант». Что он говорил, он знал, как делать то, что вы предоставили, и это не решает проблему. –

1

Вы можете использовать указатель для элемента. Объявите аргумент std::string CustomClass::*pField в своей функции, передайте его &CustomClass::criteriaN, зайдите в него с помощью anObject.*pField.

Дополнительная информация по теме: Pointers to data members.

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

--- EDIT: добавлен раствор (C++ 14 только) для разных типов критериев ---

Немного иначе, если " критерии "имеют разные типы.

Решение работает, но в C++ 14, благодаря auto и decltype().

К примеру, если

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

и комбинируя auto с decltype(), вы можете изменить 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); 

пс: Внимание: Код не испытанной

ps2 : извините за мой плохой Английский

+0

Почему не шаблон на 'enum {CRITERIA1, CRITERIA2, ...}' вместо int? Затем вы можете использовать 'getDataByCriteria (ccVec)' (с именем фактических критериев, используемым вместо 'CRITERIA1') и т. Д. Вместо магических чисел. – user4815162342

0

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

Это возможное решение, использующее 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