5

I have a class, with a function which returns a count, like this:

class CTestClass
{
public:
    // ...
    size_t GetCount()const;
    // ...
};

And somewhere in my program I have a vector of objects of that class. I have a function to get the total count (sum of results of CTestClass::GetCount()), implemented like a normal loop:

size_t sum = 0;
for(vector<CTestClass>::const_iterator it = v.begin(); it != v.end(); ++it)
{
    sum += it->GetCount();
}

I want to refactor it to use the facilities available in the standard library, and I thought of accumulate. I've been able to do it by using a function object (easy), but I'm almost sure it can be done without declaring another object (I don't have C++11 or boost, so no lambdas, but I have TR1).
When looking for an answer, I've found these resources, but they don't solve the question:

  • This is pretty much the same question, and the answers provided are a loop, accumulate and functor, and accumulate and lambda, but there's an unanswered reference to bind and the like.
  • This answer to a similar question uses accumulate, plus and bind, but uses a data member instead of a member function.

So, is there a way to do this using bind, or something similar?

Community
  • 1
  • 1
MikMik
  • 3,426
  • 2
  • 23
  • 41

2 Answers2

7

I think the following should work:

using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;

accumulate(v.begin(), v.end(), 0,
           bind(plus<size_t>(), _1, bind(&C::GetCount, _2))
);
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
fschoenm
  • 1,391
  • 13
  • 32
  • 1
    I think it should be `std::tr1::placeholders`, because he has `tr1`. (I'm not sure, though). – Nawaz Dec 07 '11 at 12:27
  • 1
    @fschoenm: I've reformatted the code so the predicate stands out a bit more (and there is no need for horizontal scrolling). Feel free to revert if you find it ugly. – Matthieu M. Dec 07 '11 at 12:32
  • This is one of the things I've tried, adapting one of the references I mentioned in the question, but the compiler (VS2008) complains with: "error C2664: '_Ret std::tr1::_Callable_obj<_Ty,_Indirect>::_ApplyX<_Ret,_Barg0,unsigned int>(_Arg0 &,_Arg1 &) const' : no se puede convertir el parámetro 2 de 'unsigned int' a 'unsigned int &" which translates as "cannot convert parameter 2 from 'unsigned int' to 'unsigned int&' – MikMik Dec 07 '11 at 13:12
  • It seems like VS2008's implementation of bind is buggy. That's why I've had problems with it. – MikMik Dec 15 '11 at 14:51
2

This works, too:

class CTestClass
{
public:
    // ...
    size_t GetCount()const;
private:
    operator size_t() { return GetCount(); }
    // ...
    template <typename InputIterator, typename T>
    friend T std::accumulate(InputIterator, InputIterator, T);

};


std::vector<CTestClass> v;
// fill vector

size_t result = std::accumulate(v.begin(), v.end(), 0);

No TR1, no bind :)

jrok
  • 54,456
  • 9
  • 109
  • 141
  • 1
    +1 ...but: You usually *should not* add an otherwise meaningless conversion operator to a class. For example, if the class is `CUniversity`, and GetCount is `GetEnrollment`, then such an operator would mean that you can convert a university to an unsigned integer :) – kol Dec 07 '11 at 12:54
  • @kol you've got a point. I updated the example, making it a bit safer. – jrok Dec 07 '11 at 13:33
  • Thanks for your original answer, but I was thinking of something more general and which does not require to change CTestClass. I mean, what if I cannot change it (i.e. it's part of an API, library, or anything)? Or what if I need to run accumulate on more than one member function of the class (e.g. sometimes on X() and sometimes on Y())? – MikMik Dec 07 '11 at 15:09
  • @MikMik If the class belongs to an API then you're stuck with what it exposes to you and you're best off with using bind, I guess. The method you already know (using functors) is IMHO the cleanest, though. With functors, you can also easily control what you want to be included in `accumulate`. – jrok Dec 07 '11 at 16:58