|
Boost : |
From: Aleksey Gurtovoy (agurtovoy_at_[hidden])
Date: 2002-11-19 06:51:51
Dirk Gerrits wrote:
> Seems very nice. But I feel to see the practical use of it.
The same as the "traditional" compile-time lambda - it allows you to write
typedef count_if<
list(int,char,long,int)
, and_(my_predicate(_), is_same(_,int))
>::type res;
instead of
typedef count_if<
list<int,char,long,int>
, bind<and_<>
, bind<my_predicate>
, bind<is_same,_,int>
>
>::type res;
> Is it just syntactic sugar?
The "syntactic sugar" part (round brackets) is the most innovative :), but
there are other differences/benefits as well. For one, it's much easier to
implement, partially because you don't have to fight the GCC and Borland's
"extension" of template template parameters matching rules that makes
something like this
template< typename T >
struct lambda
{
typedef T type;
};
template<
template< typename T > class F
, typename P
>
struct lambda< F<P> >
{
typedef F<P> type;
};
template<
template< typename T1, typename T2 > class F
, typename P1, typename P2
>
struct lambda< F<P1,P2> >
{
typedef F<P1,P2> type;
};
template< typename T, typename U = T > struct her;
typedef lambda< her<int> >::type t; // error!
ambiguous. Not that the new lambda with either of those compilers, though
:).
But the main source of the original lambda's implementation complexity lies
in the fact that actually it has to treat its arguments in a much more
"intellectual" way than one might think at first. Consider, for instance,
this:
#include "boost/mpl/find_if.hpp"
#include "boost/mpl/list.hpp"
#include "boost/type_traits/is_same.hpp"
using namespace boost;
using namespace mpl;
template< typename T > struct her
{
// ....
};
typedef list<int,her<int>,long> types;
// find 'her<int>' instantiation in the list
typedef find_if< types, is_same<_, her<int> > >::type type;
The semantics of the above is clear, and overall it seems quite harmless,
but if you compare the above with a similar-but-not-really
// find a type that is a [const] reference
typedef find_if< types, is_same<_, add_reference<_> > >::type type;
quite probably you'll see the problem; basically, there is no way how a
simple-minded lambda facility can distinguish between 'her<int>' and
'add_reference<_>' here - unless it checks if any of the template
instantiation's arguments is a lambda placeholder, and if it does that, then
it's no more a simple minded lambda facility :). In fact, it's a pretty
complicated one.
And if it doesn't distinguish between two, then an intuitive,
"it-has-to-work"
typedef find_if< types, is_same<_, her<int> > >::type type;
will simply fail to compile as the library will try to treat 'her<int>' as a
metafunction (instead as a "value argument" to the 'is_same<...>' lambda
expression).
If you disable the MPL's preprocessed headers and define
BOOST_MPL_NO_LAMBDA_HEURISTIC macro, you could see it for yourself :).
Well, anyway, obviously the "round lambda" form
typedef find_if< types, is_same(_, her<int>) >::type type;
doesn't have this problem - and the library doesn't even have to take any
special precautions for it.
Now, the last part - the semantic differences. Well, besides the above,
there is only one: the "round lambda" enables inline composition of
_metafunction classes_, not metafunctions. E.g. the 'is_same' name above
actually refers to a metafunction class:
struct is_same
{
template< typename T1, typename T2 > struct apply
: boost::is_same<T1,T2>
{
};
};
I haven't formed the opinion whether it's a good thing or not yet :), but I
kind of like the fact that actually the two facilities are quite orthogonal,
and together they cover pretty much all the needs for inline
composition/arguments binding of compile-time invocable entities.
> And if not, could you enlighten me on the practical use? :)
Well, I just invented it yesterday and haven't even had time to think about
it since then :), but I hope the above clarifies things a little bit.
Aleksey
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk