
Hello Jim,
"Jim Pritts" <jpritts@sdf.lonestar.org> wrote in message
Is it possible to do something like the following:
typedef boost::tuple<double, int, int> element;
std::vector<element> d;
std::vector<element>::iterator i = std::max_element(d.begin(), d.end(),
bind(std::max<double>,bind(get<0>,_2),_1));
Good question! The above won't compile, in part because get() requires three template arguments, so you can't get it's address using &get<0>. Furthermore, it's not trivial to explicitly instantiate get() correctly or to bind to tuple's member function get(), which is why I'd suggest creating a helper class--a function object--for selecting one of the elements of a tuple. To use it with Boost.Bind without explicitly specifying the return type, you'd need to do this: template <typename T,int i> class select { public: typedef typename boost::tuples::element<i,T>::type result_type; template <typename U> result_type operator()(const U& u) const { // Select the ith element of the tuple T and return it return u.get<i>(); } }; The required typedef, result_type, that enables Boost.Bind to deduce the return type of the function call operator, uses a helper class from Boost.Tuples, the class template element, which retrieves the type of a tuple element using its index. With a class template like select, you could use Boost.Bind with max_element like so: int main() { typedef boost::tuple<double,int,int> element; std::vector<element> d; // Code for adding elements omitted std::vector<element>::iterator i= std::max_element( d.begin(), d.end(), boost::bind(std::less<double>(), boost::bind(select<element,0>(),_1), boost::bind(select<element,0>(),_2))); } You'll also note that I've adjusted the binders from the example you gave. There are three binders in the call to max_element; the two innermost binders extract the first element of the tuples _1 and _2 (to be substituted with the actual arguments), and the third binder binds std::less<double>. The result of all these binders is a binary predicate, which is what std::max_element expects. Note that in your example, you had used std::max(), which does not work well as a binary predicate for max_element(). Although the above version of select works, it's rather inflexible and sometimes inefficient, because the function call operator always returns a copy of the ith element of the tuple. What if you need to assign a value to that element? To enable that, you'll need a new version of select, a class template that is only parameterized on the element index, and with two parameterized function call operators that return a copy of the element or a reference to the element, depending on the constness of the tuple. Using Boost.Type_traits, it's easy to add a reference to a type without risking forming a reference to reference (those are currently illegal in C++): template <int i> class select { public: template <typename T> typename boost::tuples::element<i,T>::type operator()(const T& t) const { // Select the ith element of the tuple t and return it return t.get<i>(); } template <typename T> typename boost::add_reference<typename boost::tuples::element<i,T>::type>::type operator()(T& t) const { // Select the ith element of the tuple t and return it return t.get<i>(); } }; This version of select works fine, but Boost.Bind is no longer able to deduce the return type of the function call operator(s), so you'll need to provide that information explicitly when using select: std::vector<element>::iterator i= std::max_element( d.begin(), d.end(), boost::bind(std::less<double>(), boost::bind<double>(select<0>(),_1), boost::bind<double>(select<0>(),_2))); If you think we're all done here, think again. ;-) When using Boost.Lambda, which also offers binders, there's an extended system for deducing return types. It comes in the form of a nested class template called sig that is responsible for providing the correct return type depending on which function call operator is being called, i.e., depending on the actual arguments to the function call operator(s). That is precisely the kind of mechanism that you need to solve the problem with the multiple function call operators of select. The following version of select is the most user-friendly version, but it is a little more complex. It uses Boost.Mpl for a compile-time if, Boost.Type_traits for adding a reference to the element type, Boost.Lambda's return type deduction system, and of course Boost.Tuples to operate on the tuple and retrieve the ith element's type: template <int i> class select { public: // Provide the return type template <typename Arg> class sig { typedef typename boost::tuples::element<1,Arg>::type argument_type; typedef typename boost::tuples::element<i,argument_type>::type element_type; public: // Select the correct return type; depends on the typedef typename boost::mpl::if_<boost::is_const<element_type>, element_type, typename boost::add_reference<element_type>::type>::type type; }; template <typename T> typename boost::tuples::element<i,T>::type operator()(const T& t) const { // Select the ith element of the tuple t and return it return t.get<i>(); } template <typename T> typename boost::add_reference<typename boost::tuples::element<i,T>::type>::type operator()(T& t) const { // Select the ith element of the tuple t and return a reference to it return t.get<i>(); } }; Using this version of select and Boost.Lambda's binders, you'll have both an easy-to-use interface and a powerful way of selecting tuple elements, both for lvalues and rvalues. Here's a complete program to demonstrate your example again. Note that the class template select requires you to include the headers "boost/tuple/tuple.hpp", "boost/type_traits.hpp", and "boost/mpl/if.hpp". For Boost.Lambda's bind(), you'll need to include "boost/lambda/bind.hpp". #include <iostream> #include <vector> #include <algorithm> #include <functional> #include "boost/tuple/tuple.hpp" #include "boost/type_traits.hpp" #include "boost/mpl/if.hpp" //#include "boost/lambda/bind.hpp" template <int i> class select { public: // Provide the return type template <typename Arg> class sig { typedef typename boost::tuples::element<1,Arg>::type argument_type; typedef typename boost::tuples::element<i,argument_type>::type element_type; public: // Select the correct return type; depends on the typedef typename boost::mpl::if_<boost::is_const<element_type>, element_type, typename boost::add_reference<element_type>::type>::type type; }; template <typename T> typename boost::tuples::element<i,T>::type operator()(const T& t) const { // Select the ith element of the tuple t and return it return t.get<i>(); } template <typename T> typename boost::add_reference<typename boost::tuples::element<i,T>::type>::type operator()(T& t) const { // Select the ith element of the tuple t and return a reference to it return t.get<i>(); } }; int main() { typedef boost::tuple<double,int,int> element; std::vector<element> d; // Code for adding elements omitted using namespace boost::lambda; std::vector<element>::iterator i= std::max_element( d.begin(), d.end(), bind(std::less<double>(), bind(select<0>(),_1), bind(select<0>(),_2))); std::cout << "max element: " << (*i).get<0>(); } Regards, Bjorn Karlsson
participants (1)
-
Bjorn.Karlsson@readsoft.com