|
Boost : |
From: Eric Niebler (eric_at_[hidden])
Date: 2006-07-10 13:24:29
<< Background ... >>
Way back in 1995, Scott Meyers issued a challenge to the C++ community
to build a better min/max utility [1]. In 2003, Andrei took up the
challenge [2], but his results were unsatisfactory for reasons both
technical and aesthetic [3].
In 2005, I wrote about some tricks you can do with the conditional
operator [4]. Andrei suggested a way the tricks could be used to finally
resolve Scott's min/max challenge. Still, the nagging lvalue/rvalue
problems remained. (Consider that for some user-defined type A, this: "A
const &a = std::min(A(1),A(2));" causes the reference to "a" to dangle.)
<< The Present ... >>
Last week, at Scott's prodding, I took the issue up again and finally
resolved the lvalue/rvalue problem. The result is a macro MAX(a,b) that
behaves *exactly* like ((a)<(b)?(b):(a)), except that it does not
reevaluate its arguments. It has the following advantages over std::max:
1) It supports both const and non-const arguments (including mixing
the two in a single call).
2) It supports arguments of different types.
3) The expression type of MAX(a,b) is naturally identical to
"((a)<(b)?(b):(a))", relying on the compiler's type promotion
rules.
4) The rvalue/lvalue-ness of MAX(a,b) is identical to
"((a)<(b)?(b):(a))", making it possible to use it on the left side
of an assignment, for instance: "MAX(a,b) = 1". This also means
that dangling references are impossible.
The code is 100% C++98 compliant, and should work on gcc and comeau.
(msvc will need some work-arounds.) The code is below.
<< The Future ... >>
Is there interest in turning this into a BOOST_MIN/BOOST_MAX library?
<< The Code >>
#include <boost/type.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/or.hpp>
#include <boost/type_traits/is_array.hpp>
#include <boost/type_traits/is_abstract.hpp>
template<typename Type>
boost::type<Type>* encode_type(Type &) { return 0; }
template<typename Type>
boost::type<Type const>* encode_type(Type const &) { return 0; }
///////////////////////////////////////////////////////////////////////////////
// max_impl
template<typename Ret, typename Left, typename Right>
struct max_impl
{
max_impl(Left &left, Right &right)
: left_(left)
, right_(right)
{}
struct private_type_ {};
// can't ever return an array or an abstract type by value
typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_<
boost::mpl::or_<boost::is_abstract<Ret>, boost::is_array<Ret> >
, private_type_
, Ret
>::type value_type;
operator value_type()
{
return this->left_ < this->right_ ? this->right_ : this->left_;
}
operator Ret &() const
{
return this->left_ < this->right_ ? this->right_ : this->left_;
}
private:
Left &left_;
Right &right_;
};
///////////////////////////////////////////////////////////////////////////////
// max_fun
template<typename Ret, typename Left, typename Right>
max_impl<Ret, Left, Right>
max_fun(Left &left, Right &right, boost::type<Ret> *)
{
return max_impl<Ret, Left, Right>(left, right);
}
template<typename Ret, typename Left, typename Right>
max_impl<Ret, Left const, Right>
max_fun(Left const &left, Right &right, boost::type<Ret> *)
{
return max_impl<Ret, Left const, Right>(left, right);
}
template<typename Ret, typename Left, typename Right>
max_impl<Ret, Left, Right const>
max_fun(Left &left, Right const &right, boost::type<Ret> *)
{
return max_impl<Ret, Left, Right const>(left, right);
}
template<typename Ret, typename Left, typename Right>
max_impl<Ret, Left const, Right const>
max_fun(Left const &left, Right const &right, boost::type<Ret> *)
{
return max_impl<Ret, Left const, Right const>(left, right);
}
#define MAX(a,b)\
(true\
? max_fun((a), (b), \
(true? 0 : encode_type(true? (a) : (b))))\
: (true? (a) : (b)))
[1] http://www.aristeia.com/Papers/C++ReportColumns/jan95.pdf
[2] http://www.ddj.com/dept/cpp/184403774
[3] Andrei's solution ignores lvalue/rvalue issues as raised in
Francis Glassborow's c++std-lib-15426, and it depends on Loki,
requiring a large amount of template code that largely
duplicates the type promotion logic already present in the
compiler.
[4] http://www.artima.com/cppsource/foreach.html
-- Eric Niebler Boost Consulting www.boost-consulting.com
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk