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
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_;

    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)\
         ? max_fun((a), (b), \
                   (true? 0 : encode_type(true? (a) : (b))))\
         : (true? (a) : (b)))

[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

Eric Niebler
Boost Consulting

Boost list run by bdawes at, gregod at, cpdaniel at, john at