Here is a simplified version of my code:
template<typename TIterator>
TIterator findMaximalPosition(TIterator begin, TIterator end)
{
TIterator result(begin);
for (TIterator it = begin + 1; it != end; ++it)
{
if ((*it)->value > (*result)->value) // Here I just need to change to "<"
result = it; // to get a findMinimalPosition
}
return result;
}
template<typename TIterator>
TIterator findMinimalPosition(TIterator begin, TIterator end)
{
// almost the same
}
This is just a simplified example. My code is full of places where two functions are the same except for a < or > sign or whether ++ or -- should be used.
My question is:
Is there a method how to reduce this duplication in code without
Destroying the readability
Decreasing the performance
?
I was thinking of using a pointer to an operator (either < or >) as a template parameter. This should not decrease performance, since the pointer would be a compile time constant. Is there some better or generally used way?
EDIT:
So what I did based on the answers was to implement:
template <typename TIterator, typename TComparison>
TIterator findExtremalPosition(TIterator begin, TIterator end,
TComparison comparison);
and then just call:
return findExtremalPosition(begin, end, std::less<double>());
and
return findExtremalPosition(begin, end, std::greater<double>());
I hope this is what you meant. I suppose that after some struggling similar solution can be done for ++ and -- operators.
I would make a general function that takes a predicate and use std::greater and std::less as argument to that function for the given type to implement findMaximalPosition and findMinimalPosition respectively.
As sugested by Ivaylo Strandjev, one possible solution is to use predicates.
So, if you change your function to work with predicates...
typename std::vector<int> vec;
template<typename TIterator, bool (*Predicate)(const TIterator &, const TIterator &)>
TIterator findPosition(TIterator begin, TIterator end)
{
TIterator result(begin);
for (TIterator it = begin + 1; it != end; ++it)
{
if (Predicate(it, result))
result = it;
}
return result;
}
... and then, you define some predicates that helps you to achieve your goal...
bool lesser(const vec::iterator &a, const vec::iterator &b)
{
return (*a) < (*b);
}
bool greater(const vec::iterator &a, const vec::iterator &b)
{
return (*a) > (*b);
}
... then you would be able to do this:
vec::iterator min = findPosition<typename vec::iterator, lesser>(v.begin(), v.end());
vec::iterator max = findPosition<typename vec::iterator, greater>(v.begin(), v.end());
The advantage is to provide any function you would found useful, not only the ones to check if an element is greater or smaller than other:
bool weird(const vec::iterator &a, const vec::iterator &b)
{
return ((*a) | (*b)) & 0x4;
}
vec::iterator weird = findPosition<typename vec::iterator, weird>(v.begin(), v.end());
Example here.
But before do this effort, check if the Algorithms library has already did the job.
I think that it looks pretty neat and simple.
Hope it helps.
Related
Consider an STL container C that is forward-iteratable. I need to access every step element, starting from idx. If C is a vector (i.e. has a random-access iterator) I can just use index arithmetic:
template <class Container>
void go(const Container& C) {
for(size_t i = idx; i<C.size(); i+=step) {
/* do something with C[i] */
}
}
However, if C does not support that, e.g. C is a list, one needs to rewrite the above solution. A quick attempt would be:
template <class Container>
void go(const Container& C) {
size_t max = C.size();
size_t i = idx;
for(auto it = std::next(C.begin(),idx); i < max; i+=step, it+=step) {
/* do something with *it */
}
}
Not much longer and it works... except that most likely it will trigger the undefined behavior. Both std::next and it+=step can potentially step way beyond the C.end() before i < max check is performed.
The solution I am currently using (not shown) is really bloated when compared to the initial for. I have separate check for the first iteration and those that follows. A lot of boilerplate code...
So, my question is, can the above pattern be written in a safe, and succinct way? Imagine you want to nest these loops 2 or 3 times. You don't want the whole page of code for that!
The code should be reasonably short
The code should have no overhead. Doing std::next(C.begin(), i) in each iteration over i is unnecessairly long, if you can just std::advance(it, step) instead.
The code should benefit from the case when it is indeed a random-access iterator when std::advance can be performed in constant time.
C is constant. I do not insert, erase or modify C within the loop.
You might use helper functions:
template <typename IT>
IT secure_next(IT it, std::size_t step, IT end, std::input_iterator_tag)
{
while (it != end && step--) {
++it;
}
return it;
}
template <typename IT>
IT secure_next(IT it, std::size_t step, IT end, std::random_access_iterator_tag)
{
return end - it < step ? end : it + step;
}
template <typename IT>
IT secure_next(IT it, std::size_t step, IT end)
{
return secure_next(it, step, end, typename std::iterator_traits<IT>::iterator_category{});
}
And then:
for (auto it = secure_next(C.begin(), idx, C.end());
it != C.end();
it = secure_next(it, step, C.end()) {
/* do something with *it */
}
Alternatively, with range-v3, you could do something like:
for (const auto& e : C | ranges::view::drop(idx) | ranges::view::stride(step)) {
/* do something with e */
}
The comment in the question about the requirements inspired me to implement this in terms of k * step instead of some other mechanism controlling the number of iterations over the container.
template <class Container>
void go(const Container& C)
{
const size_t sz = C.size();
if(idx >= sz) return;
size_t k_max = (sz - idx) / step + 1;
size_t k = 0
for(auto it = std::advance(C.begin(), idx); k < k_max && (std::advance(it, step), true); ++k) {
/* do something with *it */
}
}
One option is to adapt the iterator so that it is safe to advance past the end. Then you can use stock std::next(), std::advance(), pass it to functions expecting an iterator, and so on. Then the strided iteration can look almost exactly like you want:
template<class Container, class Size>
void iterate(const Container& c, Size idx, Size step)
{
if (unlikely(idx < 0 || step <= 0))
return;
bounded_iterator it{begin(c), c};
for (std::advance(it, idx); it != end(c); std::advance(it, step))
test(*it);
}
This is not dissimilar from the secure_next() suggestion. It is a little more flexible, but also more work. The range-v3 solution looks even nicer but may or may not be an option for you.
Boost.Iterator has facilities for adapting iterators like this, and it's also straightforward to do it directly. This is how an incomplete sketch might look for iterators not supporting random access:
template<class Iterator, class Sentinel, class Size>
class bounded_iterator
{
public:
using difference_type = typename std::iterator_traits<Iterator>::difference_type;
using value_type = typename std::iterator_traits<Iterator>::value_type;
using pointer = typename std::iterator_traits<Iterator>::pointer;
using reference = typename std::iterator_traits<Iterator>::reference;
using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
template<class Container>
constexpr explicit bounded_iterator(Iterator begin, const Container& c)
: begin_{begin}, end_{end(c)}
{
}
constexpr auto& operator++()
{
if (begin_ != end_)
++begin_;
return *this;
}
constexpr reference operator*() const
{
return *begin_;
}
friend constexpr bool operator!=(const bounded_iterator& i, Sentinel s)
{
return i.begin_ != s;
}
// and the rest...
private:
Iterator begin_;
Sentinel end_;
};
template<class Iterator, class Container>
bounded_iterator(Iterator, const Container&) -> bounded_iterator<Iterator, decltype(end(std::declval<const Container&>())), typename size_type<Container>::type>;
And for random access iterators:
template<RandomAccessIterator Iterator, class Sentinel, class Size>
class bounded_iterator<Iterator, Sentinel, Size>
{
public:
using difference_type = typename std::iterator_traits<Iterator>::difference_type;
using value_type = typename std::iterator_traits<Iterator>::value_type;
using pointer = typename std::iterator_traits<Iterator>::pointer;
using reference = typename std::iterator_traits<Iterator>::reference;
using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
template<class Container>
constexpr explicit bounded_iterator(Iterator begin, const Container& c)
: begin_{begin}, size_{std::size(c)}, index_{0}
{
}
constexpr auto& operator+=(difference_type n)
{
index_ += n;
return *this;
}
constexpr reference operator*() const
{
return begin_[index_];
}
friend constexpr bool operator!=(const bounded_iterator& i, Sentinel)
{
return i.index_ < i.size_;
}
// and the rest...
private:
const Iterator begin_;
const Size size_;
Size index_;
};
As an aside, it seems GCC produces slightly better code with this form than with my attempts at something like secure_next(). Can its optimizer reason better about indices than pointer arithmetic?
This example is shared also via gist and godbolt.
What I am trying to do:
I have a simple set union function in C++ using STL, and I'm trying to wrap it in a function that will let me perform the union of arbitrarily many sets contained in STL data structures (e.g. std::list, std::vector, std::forward_list, ...).
How I tried to do it:
To start, my simple set union:
#include <algorithm>
template <typename set_type>
set_type sunion(const set_type & lhs, const set_type & rhs)
{
set_type result;
std::set_union( lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::inserter(result, result.end()) );
return result;
}
where set_type defines some STL std::set<T>, e.g. std::set<int>.
After noticing several times that I end up needing to perform several unions on iterators of sets (in Python this would be a reduce of my sunion function over some iterable object of set_types). For instance, I might have
std::vector<std::set<int> > all_sets;
or
std::list<std::set<int> > all_sets;
etc., and I want to get the union of all sets in all_sets. I am trying to implement a simple reduce for this, which essentially does a (faster, more elegant, non-copying) version of:
sunion(... sunion( sunion( all_sets.begin(), all_sets.begin()+1 ), all_sets.begin()+2 ) , ... )
Essentially, to do this quickly, I just want to declare a set_type result and then iterate through all_sets and insert value in every set in all_sets into the result object:
template <typename set_type>
set_type sunion_over_iterator_range(const std::iterator<std::forward_iterator_tag, set_type> & begin, const std::iterator<std::forward_iterator_tag, set_type> & end)
{
set_type result;
for (std::iterator<std::forward_iterator_tag, set_type> iter = begin; iter != end; iter++)
{
insert_all(result, *iter);
}
return result;
}
where insert_all is defined:
// |= operator; faster than making a copy and performing union
template <typename set_type>
void insert_all(set_type & lhs, const set_type & rhs)
{
for (typename set_type::iterator iter = rhs.begin(); iter != rhs.end(); iter++)
{
lhs.insert(*iter);
}
}
How it didn't work:
Unfortunately, my sunion_over_iterator_range(...) doesn't work with arguments std::vector<set_type>::begin(), std::vector<set_type>::end(), which are of type std::vector<set_type>::iterator. I thought std::vector<T>::iterator returns an iterator<random_access_iterator_tag, T>. A
After compilation failed because of type incompatibility of the iterators, I looked at the stl vector source (located in /usr/include/c++/4.6/bits/stl_vector.h for g++ 4.6 & Ubuntu 11.10), and was surprised to see the typedef for vector<T>::iterator to be typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator;. I had thought that a ForwardIterator was a subtype of RandomAccessIterator, and so should be fine, but clearly I was incorrect, or I would not be here.
How I am grateful and ashamed of inciting your frustration due to my inexperience:
Apologies if I'm showing my ignorance-- I am trying to learn to be a better object oriented programmer (in the past I have simply hacked everything out in C-style code).
I'm doing my best, coach! Please help me out and spare the world from bad code that I would produce without your code ninja insight...
Here's a very naive approach:
std::set<T> result;
std::vector<std::set<T>> all_sets;
for (std::set<T> & s : all_sets)
{
result.insert(std::make_move_iterator(s.begin()),
std::make_move_iterator(s.end()));
}
This invalidates the elements in the source sets, though it doesn't actually move the element nodes over. If you want to leave the source sets intact, just remove the make_move_iterator.
Unfortunately there's no interface for std::set that lets you "splice" two sets in a way that doesn't reallocate the internal tree nodes, so this is more or less as good as you can get.
Here's a variadic template approach:
template <typename RSet> void union(RSet &) { }
template <typename RSet, typename ASet, typename ...Rest>
void union(RSet & result, ASet const & a, Rest const &... r)
{
a.insert(a.begin(), a.end());
union(result, r...);
}
Usage:
std::set<T> result
union(result, s1, s2, s3, s4);
(Similar move-optimizations are feasible here; you can even add some branching that will copy from immutables but move from mutables, or from rvalues only, if you like.)
Here's a version using std::accumulate:
std::set<T> result =
std::accumulate(all_sets.begin(), all_sets.end(), std::set<T>(),
[](std::set<T> & s, std::set<T> const & t)
{ s.insert(t.begin(), t.end()); return s; } );
This version seems to rely on return value optimisation a lot, though, so you might like to compare it to this hacked up and rather ugly version:
std::set<T> result;
std::accumulate(all_sets.begin(), all_sets.end(), 0,
[&result](int, std::set<T> const & t)
{ result.insert(t.begin(), t.end()); return 0; } );
Usually, when using iterators we don't care about the actual category. Just let the implementation sort it out. That means, just change the function to accept any type:
template <typename T>
typename std::iterator_traits<T>::value_type sunion_over_iterator_range(T begin, T end)
{
typename std::iterator_traits<T>::value_type result;
for (T iter = begin; iter != end; ++ iter)
{
insert_all(result, *iter);
}
return result;
}
Note that I have used typename std::iterator_traits<T>::value_type, which is the type of *iter.
BTW, the iterator pattern is not related to OOP. (That doesn't mean it's a bad thing).
I want to sort a vector of T according to a vector of double. That is, if I have
vector<T> a;
vector<double>b;
If a is {t1, t2, t3, t4} and b is {3, 1, 5, 2}, I want to obtain {t2, t4, t1, t3}.
I don't know how to declare the template. I'm trying something like
template<vector<class T>> vector<T> sortByArray(vector<T> a, vector<double>b)
And I don't have any idea of how to write the function body either.
Thanks.
EDIT: This is the usage of my algorithm. I don't get it right.
template <typename T> struct dataPair
{
dataPair(double s, T o)
: m_sortData(s)
, m_otherData(o)
{
}
bool operator< (const dataPair &rhs) { return (m_sortData < rhs.m_sortData); }
double m_sortData;
T m_otherData;
}
template <class T> vector<T> sortByArrayStuff(vector<T> objects, vector<double> sortNumber) {
vector<dataPair<T>> v;
for (unsigned int i = 0; i < objects.size(); i++) {
v.push_back(dataPair<T>(objects[i], sortNumber[i]));
}
sort(v.begin(), v.end());
vector<T> retVal;
for (unsigned int i = 0; i < objects.size(); i++) {
retVal.push_back(dataPair<T>(objects[i], sortNumber[i]));
}
return retVal;
};
I want to use the same template for vectors of "Points" and vectors of vectors of "Points":
vector<double> sortedAreas;
vector<Point> sortedPoints = sortByArray<vector<Point>>(points, sortedAreas);
vector<vector<Point>> sortedContours = sortByArray<vector<vector<Point>>>(contours, sortedAreas);
Error is
cannot convert parameter 1 from 'dataPair<T>' to 'cv::Point &&'
with
[
_Ty=cv::Point
]
and
[
T=cv::Point
]
Reason: cannot convert from 'dataPair<T>' to 'cv::Point'
with
[
T=cv::Point
]
What you should do is create a struct or class like this:
template <typename T> struct dataPair
{
dataPair(double s, T o)
: m_sortData(s)
, m_otherData(o)
{
}
bool operator< (const dataPair &rhs) { return (m_sortData < rhs.m_sortData); }
double m_sortData;
T m_otherData;
}
Then, you create a vector of these dataPair types
{
// your code ...
// that assumes b is is a std::vector<YourType>
// create vector and populate it
std::vector<dataPair<YourType>> v;
v.push_back(dataPair<YourType>(a[0],b[0]));
v.push_back(dataPair<YourType>(a[1],b[1]));
v.push_back(dataPair<YourType>(a[2],b[2]));
v.push_back(dataPair<YourType>(a[3],b[3]));
std::sort(v.begin(),v.end());
// your code (now they will be sorted how you like in v)
}
EDIT: had some typos
EDIT2: You can also do this with functors for more efficiency, but this is the basic idea.
EDIT3: Using functors with sort is described very nicely here. See where they use the functor myclass in which they overload operator(). This allows compile-time optimizations to be made (because from std::sort's perspective the sorting criterion is a template type)
The easiest way I can think of, is if you just included the double within your class declaration of T, and used it as your sorting parameter. Sorry if my template syntax is not that great, its been a while since ive used them:
class YourClass
{
//Some stuff...
double sortVal;
};
bool std::less<YourClass>(YourClass left, YourClass right)
{
return left.sortVal < right.sortval;
}
I was just doing something like this the other day, and here was my idea. Take both vectors, and combine them into a multimap. The sorting will be done automatically just by inserting them into the map, then you extract them from the map back into the vectors. I came up with 2 function templates for this job, here they are:
// This function basically does the reverse of a transform. Whereas transform takes
// two inputs and by some method merges them into one, this function takes one input
// and by some method splits it in two.
template<typename InIt, typename Out1It, typename Out2It, typename Fn>
void fork_transform(InIt ibegin, InIt iend, Out1It o1begin, Out2It o2begin, Fn fork)
{
while(ibegin != iend)
{
fork(*ibegin, *o1begin, *o2begin);
++o1begin;
++o2begin;
++ibegin;
}
}
template<typename ItPrimary, typename ItSecondary>
void simul_sort(ItPrimary begin1, ItPrimary end1, ItSecondary begin2)
{
typedef std::iterator_traits<ItPrimary>::value_type T1;
typedef std::iterator_traits<ItSecondary>::value_type T2;
typedef std::multimap<T1,T2> Map_t;
typedef Map_t::value_type Pair_t;
Map_t m;
// this was necessary for me because of a bug in VC10, see my most recent question
auto MakePair = [](const T1 & first, const T2 & second) { return std::make_pair(first,second); };
std::transform(begin1, end1, begin2, std::inserter(m,m.begin()), MakePair);
auto Fork = [](const Pair_t & p, T1 & first, T2 & second) { first = p.first; second = p.second; };
fork_transform(m.begin(), m.end(), begin1, begin2, Fork);
}
This actually sorts both vectors simultaneously. The first is sorted normally, the second is sorted according to the order of the first:
simul_sort(b.begin(), b.end(), a.begin());
If you need generic solution for the problem then take a look on zipper template in one of answers here:
number of matches in two sequences with STL
You will need something close to that zipper - some entity that zips two sequences into one.
Here's a generic solution - a function which returns a vector of the indexes into an array. You can use these indexes on either of your a or b to get them in sorted order.
template<class RandomAccessIterator>
struct IndirectCompare : public std::binary_function<size_t, size_t, bool>
{
IndirectCompare(RandomAccessIterator first) : m_first(first)
{
}
bool operator()(const size_t &left, const size_t &right)
{
return *(m_first + left) < *(m_first + right);
}
RandomAccessIterator m_first;
};
template<class RandomAccessIterator>
std::vector<size_t> ordered_index(RandomAccessIterator first, RandomAccessIterator last)
{
size_t n = last - first;
std::vector<size_t> result;
result.reserve(n);
for (size_t i = 0; i < n; ++i)
result.push_back(i);
IndirectCompare<RandomAccessIterator> comp(first);
std::sort(result.begin(), result.end(), comp);
return result;
}
P.S. I've tested this code now, and it works.
If you want to sort two vectors simultaneously, you should rather create a std::vector (say c) of std::pair. First component must be the one which is to be sorted normally and second must be the one to be sorted accordingly.
std::vector<std::pair<double, T>> c;
std::sort(c.begin(), c.end());
Hope this helps.
I am new to using templates and am required to use a template to do something, but don't know how to call the templated function. It's probably soething simple, but I can't see it.
template<class It, class T>
// iterator and value_type of *It
void Calc(It begin, It end, std::pair<int, int> &out)
{
std::vector<It>::iterator iter;
std::map<int, int> StartMap;
std::map<int, int>::reverse_iterator rit;
int sum, start, stop, count;
start = stop = 1;
count = sum = 0;
for(iter = begin; iter != end; iter++ )
{
sum += iter;
count++;
stop++;
if(sum <= 0)
{
// store original start
StartMap.insert(start, count);
// set new start position
start = stop;
}
}
// set iterator to highest value
rit = StartMap.rbegin();
start = rit->first;
stop = start + rit->second;
out.insert(start, stop);
}
but not sure how I call it with 2 std::vector iterators.
I've tried this
void doSomething(std::vector<int>& stopsVec)
{
std::pair<int, int> out;
Calc<std::vector<int>::iterator, std::pair<int, int>>(stopsVec.begin(), stopsVec.end(), &out);
}
void doSomething(std::vector<int>& stopsVec)
{
std::pair<int, int> out;
Calc<std::vector<int>::iterator, std::pair<int, int> >
(stopsVec.begin(), stopsVec.end(), out); // not &out
}
Calc takes a reference to std::pair<int, int>, so you want to just give it out. Passing &out tries to pass a pointer to a pair - which won't work.
EDIT
assuming the signature is actually:
template<class It>
void Calc(It begin, It end, std::pair<int, int> &out)
You can call it with:
Calc(stopsVec.begin(), stopsVec.end(), out);
The compiler can deduce the correct template type from the parameters, without requiring you to specify them between <>
EDIT
Keith makes a good point below. That's another compilation error you would have here. Also note that:
sum += iter;
does not do what you want. you probably meant:
sum += *iter;
But since sum is an int, and iter is a template type, this is not really a general-purpose template method. It's really only going to work for iterators over numeric types.
And, one other issue:
Calc<std::vector<int>::iterator, std::pair<int, int> > // use a space
(stopsVec.begin(), stopsVec.end(), out);
instead of
Calc<std::vector<int>::iterator, std::pair<int, int>> // ">>" is shift operator
(stopsVec.begin(), stopsVec.end(), out);
You need a space between the closing > signs in order to have template syntax. Otherwise you're doing bitshift (or stream extraction), and the compiler will get confused because nothing will make sense from that point on.
Note that:
template<class It, class T>
void Calc(It begin, It end, std::pair<int, int> &out)
{
std::vector<It>::iterator iter;
for(iter = begin; iter != end; iter++ )
is wrong. It should probably be:
template<class It, class T>
void Calc(It begin, It end, std::pair<int, int> &out)
{
It iter;
// etc.
for(iter = begin; iter != end; iter++ )
But also note that in C++, it is generally preferred to follow the 'declaration is initialisation' approach, so this becomes:
template<class It, class T>
void Calc(It begin, It end, std::pair<int, int> &out)
{
// etc.
for(It iter = begin; iter != end; iter++ )
You don't need to explicitly pass the type being iterated over as a template argument. The STL designers were quite wise and realized that this often comes up, and there's a (not very pretty but entirely correct) way to introspect on the type of an iterator to get it's underlying type as follows:
typedef typename std::iterator_traits<It>::value_type value_type;
Once you've done this, you can use the name value_type to refer to the type being iterated over. This lets you rewrite the template function as
template <typename It>
void Calc(It begin, It end, std::pair<int, int>& out) {
typedef typename std::iterator_traits<It>::value_type value_type;
/* ... Rest of the code, now using this type ... */
}
And to seal the deal, now that there aren't any auxiliary types required, you can call the function directly as
std::vector<int> v = /* ... */
std::pair<int, int> result;
Calc(v.begin(), v.end(), result);
Hopefully this is easier to read and write!
Searching in the second value of a map i use somthing like the following:
typedef std::map<int, int> CMyList;
static CMyList myList;
template<class t> struct second_equal
{
typename typedef t::mapped_type mapped_type;
typename typedef t::value_type value_type;
second_equal(mapped_type f) : v(f) {};
bool operator()(const value_type &a) { return a.second == v;};
mapped_type v;
};
...
int i = 7;
CMyList::iterator it = std::find_if(myList.begin(), myList.end(),
second_equal<CMyList>(i));
Question: How can i do such a find in a single line without supplying a self written template?
Use a selector to select the first or the second element from the value_type that you get from the map.
Use a binder to bind the value (i) to one of the arguments of the std::equal_to function.
Use a composer to use the output of the selector as the other argument of the equal_to function.
//stl version
CMyList::iterator it = std::find_if(
myList.begin(),
myList.end(),
std::compose1(
std::bind2nd(equal_to<CMyList::mapped_type>(), i),
std::select2nd<CMyList::value_type>())) ;
//Boost.Lambda or Boost.Bind version
CMyList::iterator it = std::find_if(
myList.begin(),
myList.end(),
bind( &CMyList::mapped_type::second, _1)==i);
I am going to be off, voluntarily. The problem with lambda's is that (apart from C++0x) you cannot actually use something like _.second at the moment.
Personally, I thus use:
template <class Second>
class CompareSecond
{
public:
CompareSecond(Second const& t) : m_ref(t) {} // actual impl use Boost.callparams
template <class First>
bool operator()(std::pair<First,Second> const& p) const { return p.second == m_ref; }
private:
Second const& m_ref;
};
Which I combine with:
template <class Second>
CompareSecond<Second> compare_second(Second const& t)
{
return CompareSecond<Second>(t);
}
In order to get automatic type deduction.
And this way I can just write
CMyList::iterator it = std::find_if(myList.begin(), myList.end(), compare_second(i));
True, it does not use binders.
But at least, mine is readable and easily understandable, which beats the crap out of clever trickery in my opinion.
Note:
actually I went as far as wrapping STL algorithms to take full containers, so it would be:
CMyList::iterator it = toolbox::find_if(myList, compare_second(i));
which (imho) is clearly as readable as you can get without the auto keyword for type inference.
You can use Boost Lambda
CMyList::iterator it = std::find_if(
myList.begin(), myList.end(),
boost::lambda::bind(&CMyList::value_type::second, boost::lambda::_1) == i);
You can turn this problem around and just write your own algorithm and use it instead. This way you are not stuck with writing lots of little functors.
template <typename Iter, typename T>
Iter find_second(Iter first, Iter last, T value) {
while (first != last) {
if (first->second == value) {
return first;
}
++first;
}
return first;
}
Note this isn't tested or even compiled.
It seems to me that solving this with binders is just asking for lots of ugly code. What you are really asking for is a new algorithm so just add the algorithm. With that said, I would probably end up implementing something like Matthieu M. came up with.