Boost logo

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