简体   繁体   中英

Template function for collection based on member

I have the following structures

struct Obj
{
  int a;
  int b;
};
class ObjCollection
{
   map<int,Obj> collMap;
 public:
   string getCsvA();
   string getCsvB();
};

getCsvA returns a csv of all the a values in the objects of collMap . getCsvB returns the same for all the b values.

Is there a way I can template this function? The reason it becomes complicated for me is that i cannot pass the address of which member i want to generate the csv for, from outside this class ie from my client code. Is there a way to do this? Note: I can not use C++11.

This looks like you need a function as a parameter to getCsv rather than templates:

Declare the function as string getCsv(int (*selectMember)(Obj)) . Furthermore use selectMember([...]) wherever you would have used [...].a in getCsvA .

Now you can call getCsv providing a method returning the right field of Obj , for example:

int selectA(Obj o)
{
    return o.a;
}

While a bit inelegant, if you've just a couple fields you can reasonably have getCsvA and getCsvB call a getCsv(A_or_B a_or_b) function, given enum A_or_B { A, B }; , then inside getCsv when you you're iterating over collMap say int value = (a_or_b == A) ? iterator->a : iterator->b; int value = (a_or_b == A) ? iterator->a : iterator->b; , then you put that value into your csv string. Easier than worrying about pointers to member data, functors or templates: when you're comfortable with this level of programming, then you can worry about more abstract approaches (hopefully you'll have C++11 available then, as lambdas are nice for this).

The skeleton of code that you have actually looks okay. It's generally a bad idea to parameterize things that aren't necessary to parameterize. Templates are the most powerful way, but they should really be used with equal discretion.

By adding parameters, you add more opportunities for incorrect code (incorrect function pointer, null pointer, etc...). Using function pointers or virtual methods also creates more difficulty for the compiler in optimizing, since the code executing generally has to be resolved at runtime.

If you were using C++11 instead of C++03 though, using a std::tuple instead of a naked struct would probably make sense, and you would get a templated function as a bonus.

#include <utility>

template<typename... Ts>
class TupleCollection {

 public:

   template<std::size_t I>
   std::string getCsv() {

     for (const auto& p : collMap) {

       std::string v = static_cast<std::string>(std::get<I>(p.second));
       ...
      }
  }

  private:

    std::map<int, std::tuple<Ts...>> collMap;

};

Then getting the CSV for the relevant field in a compile time safe way would be

tc.getCSV<0>();
tc.getCSV<1>();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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