I work a lot with pairs of values: std::pair<int, int> my_pair. Sometimes I need to perform the same operation on both my_pair.first and my_pair.second.
My code would be much smoother if I could do my_pair[j] and loop over j=0,1.
(I am avoiding using arrays because I don't want to bother with allocating memory, and I use pair extensively with other things).
Thus, I would like to define operator[] for std::pair<int, int>.
And I can't get it to work, (I'm not very good with templates and such)...
#include <utility>
#include <stdlib.h>
template <class T1> T1& std::pair<T1, T1>::operator[](const uint &indx) const
{
if (indx == 0)
return first;
else
return second;
};
int main()
{
// ....
return 0;
}
fails to compile. Other variations fail as well.
As far as I can tell, I am following the Stack Overflow operator overloading FAQ, but I guess I am missing something...
you cannot overload operator[] as a non-member
you cannot define a member function which has not been declared in the class definition
you cannot modify the class definition of std::pair
Here's a non-member implementation:
/// #return the nth element in the pair. n must be 0 or 1.
template <class T>
const T& pair_at(const std::pair<T, T>& p, unsigned int n)
{
assert(n == 0 || n == 1 && "Pair index must be 0 or 1!");
return n == 0 ? p.first: p.second;
}
/// #return the nth element in the pair. n must be 0 or 1.
template <class T>
T& pair_at(std::pair<T, T>& p, unsigned int index)
{
assert(index == 0 || index == 1 && "Pair index must be 0 or 1!");
return index == 0 ? p.first: p.second;
}
// usage:
pair<int, int> my_pair(1, 2);
for (int j=0; j < 2; ++j)
++pair_at(my_pair, j);
Note that we need two versions: one for read-only pairs and one for mutable pairs.
Don't be afraid to use non-member functions liberally. As Stroustrup himself said, there is no need to model everything with an object or augment everything through inheritance. If you do want to use classes, prefer composition to inheritance.
You can also do something like this:
/// Applies func to p.first and p.second.
template <class T, class Func>
void for_each_pair(const std::pair<T, T>& p, Func func)
{
func(p.first);
func(p.second);
}
/// Applies func to p.first and p.second.
template <class T, class Func>
void for_each_pair(std::pair<T, T>& p, Func func)
{
func(p.first);
func(p.second);
}
// usage:
pair<int, int> my_pair(1, 2);
for_each_pair(my_pair, [&](int& x){
++x;
});
That isn't too unwieldy to use if you have C++11 lambdas and is at least a bit safer since it has no potential to access out of bounds.
You cannot add functions to an existing class like this. And you certainly can't do it to things in the std namespace.
So you should define your own wrapper class:
class MyPair {
private:
std::pair<int,int> p;
public:
int &operator[](int i) { return (i == 0) ? p[0] : p[1]; }
// etc.
};
You probably should check out Boost.Fusion. You can apply algorithms to sequences(which std::pair is considered a sequence). So for example you can do for_each like this:
std::pair<int, int> my_pair;
for_each(my_pair, [] (int i)
{
cout << i;
});
You can also access the index of the element like this:
int sum = at_c<0>(my_pair) + at_c<1>(my_pair);
Related
Suppose I want to write a recursive template function that compares an individual value to every single element in an n-dimensional vector, returning true if there is at least one match and false if there are none.
I wrote some code to do this, although it's probably far from optimal:
template <typename T, typename Checker>
void check_for_each(const T& v, const Checker condition)
{
condition(v);
}
template <typename T, typename Checker>
void check_for_each(const std::vector<T>& v, const Checker condition)
{
for(unsigned int i = 0; i < v.size(); i++)
{
check_for_each(v[i], condition);
}
}
template <typename T, typename U>
bool is_equal_any(const T& VALUE, const std::vector<typename U> VECTOR)
{
bool is_equal = false;
check_for_each(VECTOR, [&is_equal, &VALUE](const T& val)
{
if(!is_equal && VALUE == val)
{
is_equal = true;
}
});
return is_equal;
}
While this seems to work, I've encountered an unusual issue and I can't quite understand it. For example, the following code works:
enum PIECE_NAME {EMPTY, PAWN, ROOK, KNIGHT, BISHOP, QUEEN, KING};
std::vector<std::vector<int>> board {{ROOK, BISHOP}, {KNIGHT, QUEEN}};
std::cout << is_equal_any(2, board); // outputs 1 (there is a rook on the board)
Yet the following, slight change does not:
std::cout << is_equal_any(ROOK, board); // compile error C2664
Apparently my function cannot figure out how to convert the enum value to an integer. Of course, I can just use static_cast<int>(ROOK), and the code compiles and runs as expected, but that's obviously not ideal. Furthermore, I know that I can change my board's declaration to std::vector<std::vector<PIECE_NAME>> board, (which also runs as expected) but I'd prefer to leave it at int. So is it possible to rewrite these recursive template functions so that is_equal_any can take enum values directly? I'm still very new to C++, so I would really appreciate as much detail in your answer as possible. Thank you.
The problem arises from the type T here:
check_for_each(VECTOR, [&is_equal, &VALUE](const T& val)
^
By calling
is_equal_any(ROOK, board)
T is a PIECE_NAME, but what you are finally passing as parameter to this lambda are the elements of your vector of type int. But an int can't be implicitly converted to an enum.
You can't either use directly U as it could be a std::vector<int> or std::vector< std::vector<int> > or...
If you were using C++14, you could use a generic lambda with auto:
check_for_each(VECTOR, [&is_equal, &VALUE](const auto& val)
But as you tagged your question C++11, you can use a trait:
template <typename T>
struct leaf_type {
using type = T;
};
template <typename T>
struct leaf_type<std::vector<T>> {
using type = typename leaf_type<T>::type;
};
template <typename T>
using leaf_type_t = typename leaf_type<T>::type;
usage:
check_for_each(VECTOR, [&is_equal, &VALUE](const leaf_type_t<U> & val)
DEMO
Btw you should avoid nested std::vectors and linearize it into a single one like:
std::vector<int> board {ROOK, BISHOP, KNIGHT, QUEEN};
Then you can easily use std::find.
This is kind of an XY problem, as there are better approaches:
Use a scoped enum
don't mix integers and enums
delegate your work to std::any_of
For example:
namespace multi_dim{
template< class InputIt, class UnaryPredicate >
bool any_of(InputIt first, InputIt last, UnaryPredicate p)
{
using std::any_of;
for(;first != last; ++first)
{
bool next = any_of(first->cbegin(), first->cend(), p);
if (next)
return true;
}
return false;
}
}
Demo
A test:
std::vector<std::vector<PIECE>> board {{PIECE::ROOK, PIECE::BISHOP}, {PIECE::KNIGHT, PIECE::QUEEN}};
std::cout << std::boolalpha << multi_dim::any_of(board.cbegin(), board.cend(), [](PIECE p){return p == PIECE::ROOK;}) << std::endl;
std::cout << std::boolalpha << multi_dim::any_of(board.cbegin(), board.cend(), [](PIECE p){return p == PIECE::EMPTY;}) << std::endl;
Output:
true
false
Wild guess: Try a difference template parameter for VALUE and for the element type of VECTOR.
I don't use MSVC so I'm not sure exactly what error you're getting.
... regardless of all that, I just have to repeat my comment: Please don't write this kind of code.
Although the solution using std::any_of is the best one, I give my answer that show somewhat less drastic improvement to original code.
template <typename T, typename Checker>
bool check_for_each(const T& v, const Checker condition)
{
return condition(v);
}
template <typename T, typename Checker>
bool check_for_each(const std::vector<T>& v, const Checker condition)
{
return std::find_if(begin(v), end(v), [condition](const T &t) { return check_for_each(t, condition); }) != v.end();
}
template <typename T, typename U>
bool is_equal_any(const T& value, const U &container)
{
return check_for_each(container, [&value](const T& val){ return value == val; });
}
enum class PIECE_NAME { EMPTY, PAWN, ROOK, KNIGHT, BISHOP, QUEEN, KING };
void test()
{
std::vector<std::vector<PIECE_NAME>> board
{
{ PIECE_NAME::ROOK, PIECE_NAME::BISHOP },
{ PIECE_NAME::KNIGHT, PIECE_NAME::QUEEN }
};
std::cout << is_equal_any(PIECE_NAME::ROOK, board);
}
This solution is still hard-coded for nested vectors but the code has been simplified a bit and also optimized as it will stop searching once an item is found.
As already suggested in other comments, you should really use enum (or better yet enum class). It does not make much sense to store integers in the vector as you loose type information.
I am writing a template function, with template argument X. Inside that function, I want to create std::set<std::pair<int, X>> such that:
objects in that set are sorted by the first (int) field of the pair (I don't care about how ties are broken)
I can add multiple objects with the same .first to the set, as long as their .second are not identical
If I knew that template argument X always has < defined, the most basic std::set<std::pair<int, X> (with the default comparator) would have worked perfectly fine. Unfortunately, I cannot assume anything about X.
I was thinking to "cheat" and use pointer-based comparison for the X field:
template <typename X>
struct DistCmp {
bool operator()(const std::pair<int, X>& lhs, const std::pair<int, X>& rhs) const {
return lhs.first < rhs.first || lhs.first == rhs.first && &lhs.second < &rhs.second;
}
};
template <typename X>
void f() {
std::set<std::pair<int, X>, DistCmp<X>> s{};
// ...
}
(After all, I don't really care how the .second is compared, as long as it's not compared equally for non-identical objects.)
Unfortunately, I don't think it's correct. Partly because of the quote from C++ standard here (it suggests that pointer comparison, in general, is unspecified, so I can't rely on them being unequal for non-identical objects). And partly I just feel it's suspicious/hacky.
Is there any clean/portable solution for this issue?
Update:
One approach I thought about was to use == for the comparison of pointers instead of <. However, this is no good because it will cause pair<1, x> < pair<1, y> && pair<1, y> < pair<1, x> to be true. This violates the requirement of strict weak ordering and may cause stuff to break.
Short answer: use std::multiset and sidestep whole issue - multiset allows multiple keys, so just compare on first element of pair.
Note, that if you want to disallow the same VALUE of X in your map, you've to add requirement to X, so it were at least EqualityComparable. Otherwise you can't even detect, when values of X are the same and when are different.
Longer answer:
Your code won't produce the result you're hoping for. Consider adding new pair <0, x> to map with <0, x>. std::map will try to find existence of <0, x> using temporary copy of <0, x>. It will use address of x (temporary!), it will compare false to everything, that is inside in map and std::map will find a place to insert based on address of x (temporary!). Then it will COPY x, thus changing address and possibly breaking its own ordering.
I think ::std::map< int, ::std::set< X > > satisfies both of your criteria.
Update: since there might be no < operator to compare X instance you may want to write a template with specialization:
#include <map>
#include <set>
#include <vector>
#include <type_traits>
#include <algorithm>
#include <utility>
// Default implementation is used when there is no < operator to compare T instances,
// but there is == operator.
template< typename T, typename TDummy = void >
t_FancyContainerHelper final
{
public: using
t_Values = ::std::vector< T >;
public: using
t_IntToValues = ::std::map< int, t_Values >;
public: static void
Insert(t_IntToValues & int_to_values, int const key, T const & value)
{
auto p_pair{int_to_values.find(key)};
if(int_to_values.end() != p_pair)
(
auto const & values(p_pair->second);
if(values.end() != ::std::find(values.begin(), values.end(), value))
{
return;
}
}
else
{
p_pair = int_to_values.emplace
(
::std::piecewise_construct
, ::std::forward_as_tuple(key)
, ::std::forward_as_tuple()
).second;
}
auto & values(p_pair->second);
values.emplace(value);
}
};
// This specialization is used when there is < operator to compare T instances.
template< typename T > class
t_FancyContainerHelper
<
T
, ::std::enable_if_t
<
::std::is_same
<
bool
, decltype
(
::std::declval< T const & >()
<
::std::declval< T const & >()
)
>
>
> final
{
public: using
t_Values = ::std::set< T >;
public: using
t_IntToValues = ::std::map< int, t_Values >;
public: static void
Insert(t_IntToValues & int_to_values, int const key, T const & value)
{
auto p_pair{int_to_values.find(key)};
if(int_to_values.end() != p_pair)
(
auto const & values(p_pair->second);
if(values.end() != values.find(value))
{
return;
}
}
else
{
p_pair = int_to_values.emplace
(
::std::piecewise_construct
, ::std::forward_as_tuple(key)
, ::std::forward_as_tuple()
).second;
}
auto & values(p_pair->second);
values.emplace(value);
}
};
What is the best way to write generic code for targeting C arrays, C++ libraries, and C++ standard library?
Example: dot product
template<class Vector1, class Vector2>
constexpr auto dot_product(Vector1 const& v1, Vector2 const& v2) {
// doesn't work for Vectors that do not implement () subscripting
using return_type = decltype(v1(0) + v2(0));
return_type tmp = return_type{};
// doesn't work for e.g. std::tuple
for (std::size_t i = 0, e = size(v1); i != e; ++i) {
tmp += v1(i) * v2(i);
}
return tmp;
}
There is a problem with access the elements:
for a C array array[i], C matrix array[i][j]
for a C++ std::vector, vector[i]
for a C++ tuple, std::get<i>(tuple)
for a C++ linear algebra vector/matrix (e.g. Eigen): vector(i), matrix(i, j)
There is a problem with iteration:
run-time for loops for C arrays, std::vectors, ...
boost::fusion for e.g. std::array, std::tuple, ...
custom expression for linear algebra libraries that handle both cases under the covers (e.g. Eigen uses expression templates)
Boost.Geometry uses a get function to solve the access problem which results in convoluted code full with gets everywhere. It also uses strategies for dispatching the different iteration methods.
Are there any better alternatives?
You can work with policy based template.
template <typename T>
struct DefaultUsePolicy {
int DoSomethingWithT( T const & ) {
return 42;
}
};
template < typename T, typename UsePolicy = DefaultUsePolicy<T>>
int Generic( T& arg ) {
UsePolicy use;
return use.DoSomethingWithT(arg);
}
Then have some default implementation for common type and let the user write a policy if he have a custom type.
The Generic function will document the services need in the policies it requires.
It is similar to what is done with std::unique_ptr and std::default_deleter to provide control on the destruction of the pointer owned.
You may wrap the get function inside a class, something like:
#define Return(ret) decltype ret { return ret; }
template <typename T>
class Getter
{
private:
T& t;
public:
constexpr explicit Getter(T&t) : t(t) {}
constexpr auto operator () (std::size_t i) const
-> Return((get(t, i)))
constexpr auto operator () (std::size_t i, std::size_t j) const
-> Return((get(t, i, j)))
operator T&() const { return t; }
};
template <typename T>
Getter<T> make_getter(T&t) { return Getter<T>(t); }
And then
template<class Vector1, class Vector2>
constexpr auto dot_product(Vector1 const& v1_arg, Vector2 const& v2_arg) {
auto v1 = make_getter(v1_arg);
auto v2 = make_getter(v2_arg);
using return_type = decltype(v1(0) + v2(0));
return_type tmp = return_type{};
for (std::size_t i = 0, e = size(v1); i != e; ++i) {
tmp += v1(i) * v2(i);
}
return tmp;
}
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 have trouble describing my problem so I'll give an example:
I have a class description that has a couple of variables in it, for example:
class A{
float a, b, c, d;
}
Now, I maintain a vector<A> that contains many of these classes. What I need to do very very often is to find the object inside this vector that satisfies that one of it's parameters is maximal w.r.t to the others. i.e code looks something like:
int maxi=-1;
float maxa=-1000;
for(int i=0;i<vec.size();i++){
res= vec[i].a;
if(res > maxa) {
maxa= res;
maxi=i;
}
}
return vec[maxi];
However, sometimes I need to find class with maximal a, sometimes with maximal b, sometimes the class with maximal 0.8*a + 0.2*b, sometimes I want a maximal a*VAR + b, where VAR is some variable that is assigned in front, etc. In other words, I need to evaluate an expression for every class, and take the max. I find myself copy-pasting this everywhere, and only changing the single line that defines res.
Is there some nice way to avoid this insanity in C++? What's the neatest way to handle this?
Thank you!
I know this thread is old, but i find it quite useful to implement a powerful argmax function in C++.
However, as far as i can see, all the given examples above rely on std::max_element, which does comparison between the elements (either using a functor or by calling the operator<). this can be slow, if the calculation for each element is expensive. It works well for sorting numbers and handling simple classes, but what if the functor is much more complex? Maybe calculating a heuristic value of a chess position or something else that generate a huge tree etc.
A real argmax, as the thread starter mentioned, would only calculate its arg once, then save it to be compared with the others.
EDIT: Ok i got annoyed and had too much free time, so i created one < C++11 and one C++11 version with r-value references, first the C++11 version:
#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
template<typename IteratorT, typename HeuristicFunctorT>
IteratorT argmax(IteratorT && it, const IteratorT & end, const HeuristicFunctorT & functor) {
IteratorT best(it++);
typename HeuristicFunctorT::result_type best_value(functor(*best));
for(; it != end; ++it) {
typename HeuristicFunctorT::result_type value(functor(*it));
if (value > best_value) {
best_value = value;
best = it;
}
}
return best;
}
template<typename IteratorT, typename HeuristicFunctorT>
inline IteratorT argmax(const IteratorT & begin, const IteratorT & end, const HeuristicFunctorT & functor) {
return argmax(IteratorT(begin), end, functor);
}
class IntPairFunctor : public std::unary_function< std::pair<int, int>, int > {
public:
int operator() (const std::pair<int, int> & v) const {
return v.first + v.second;
}
};
std::pair<int, int> rand_pair() {
return std::make_pair(rand(), rand());
}
int main(int argc, const char **argv) {
srand(time(NULL));
std::vector< std::pair<int, int> > ints;
std::generate_n(std::back_insert_iterator< std::vector< std::pair<int, int> > >(ints), 1000, rand_pair);
std::vector< std::pair<int, int> >::iterator m (argmax(ints.begin(), ints.end(), IntPairFunctor()));
std::cout << std::endl << "argmax: " << *m << std::endl;
}
The non C++11 version is much simpler, only the template:
template<typename IteratorT, typename HeuristicFunctorT>
IteratorT argmax(IteratorT it, const IteratorT & end, const HeuristicFunctorT & functor) {
IteratorT best(it++);
typename HeuristicFunctorT::result_type best_value(functor(*best));
for(; it != end; ++it) {
typename HeuristicFunctorT::result_type value(functor(*it));
if (value > best_value) {
best_value = value;
best = it;
}
}
return best;
}
Note that neither version requires any template arguments, the only requirement is that the heuristic implements the unary_function class
template <typename F>
struct CompareBy
{
bool operator()(const typename F::argument_type& x,
const typename F::argument_type& y)
{ return f(x) < f(y); }
CompareBy(const F& f) : f(f) {}
private:
F f;
};
template <typename T, typename U>
struct Member : std::unary_function<U, T>
{
Member(T U::*ptr) : ptr(ptr) {}
const T& operator()(const U& x) { return x.*ptr; }
private:
T U::*ptr;
};
template <typename F>
CompareBy<F> by(const F& f) { return CompareBy<F>(f); }
template <typename T, typename U>
Member<T, U> mem_ptr(T U::*ptr) { return Member<T, U>(ptr); }
You need to include <functional> for this to work. Now use, from header <algorithm>
std::max_element(v.begin(), v.end(), by(mem_ptr(&A::a)));
or
double combination(A x) { return 0.2 * x.a + 0.8 * x.b; }
and
std::max_element(v.begin(), v.end(), by(std::fun_ptr(combination)));
or even
struct combination : std::unary_function<A, double>
{
combination(double x, double y) : x(x), y(y) {}
double operator()(const A& u) { return x * u.a + y * u.b; }
private:
double x, y;
};
with
std::max_element(v.begin(), v.end(), by(combination(0.2, 0.8)));
to compare by a member or by linear combinations of a and b members. I split the comparer in two because the mem_ptr thing is damn useful and worth being reused. The return value of std::max_element is an iterator to the maximum value. You can dereference it to get the max element, or you can use std::distance(v.begin(), i) to find the corresponding index (include <iterator> first).
See http://codepad.org/XQTx0vql for the complete code.
This is what functors and STL are made for:
// A class whose objects perform custom comparisons
class my_comparator
{
public:
explicit my_comparator(float c1, float c2) : c1(c1), c2(c2) {}
// std::max_element calls this on pairs of elements
bool operator() (const A &x, const A &y) const
{
return (x.a*c1 + x.b*c2) < (y.a*c1 + y.b*c2);
}
private:
const float c1, c2;
};
// Returns the "max" element in vec
*std::max_element(vec.begin(), vec.end(), my_comparator(0.8,0.2));
Is the expression always linear? You could pass in an array of four coefficients. If you need to support arbitrary expressions, you'll need a functor, but if it's just an affine combination of the four fields then there's no need for all that complexity.
You can use the std::max_element algorithm with a custom comparator.
It's easy to write the comparator if your compiler supports lambda expressions.
If it doesn't, you can write a custom comparator functor. For the simple case of just comparing a single member, you can write a generic "member comparator" function object, which would look something like this:
template <typename MemberPointer>
struct member_comparator
{
MemberPointer p_;
member_comparator(MemberPointer p) : p_(p) { }
template <typename T>
bool operator()(const T& lhs, const T& rhs) const
{
return lhs.*p_ < rhs.*p_;
}
};
template <typename MemberPointer>
member_comparator<MemberPointer> make_member_comparator(MemberPointer p)
{
return member_comparator<MemberPointer>(p);
}
used as:
// returns an iterator to the element that has the maximum 'd' member:
std::max_element(v.begin(), v.end(), make_member_comparator(&A::d));
You could use the std::max_element STL algorithm providing a custom comparison predicate each time.
With C++0x you can even use a lambda function for it for maximum conciseness:
auto maxElement=*std::max_element(vector.begin(), vector.end(), [](const A& Left, const A& Right) {
return (0.8*Left.a + 0.2*Left.b)<(0.8*Right.a + 0.2*Right.b);
});
Sample of using max_element/min_element with custom functor
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
struct A{
float a, b, c, d;
};
struct CompareA {
bool operator()(A const & Left, A const & Right) const {
return Left.a < Right.a;
}
};
int main() {
vector<A> vec;
vec.resize(3);
vec[0].a = 1;
vec[1].a = 2;
vec[2].a = 1.5;
vector<A>::iterator it = std::max_element(vec.begin(), vec.end(), CompareA());
cout << "Largest A: " << it->a << endl;
it = std::min_element(vec.begin(), vec.end(), CompareA());
cout << "Smallest A: " << it->a << endl;
}