Boost logo

Boost Users :

From: Joaquin M Lopez Munoz (joaquin_at_[hidden])
Date: 2007-03-29 17:12:36


Scott Meyers <usenet <at> aristeia.com> writes:

>
> Joaquín Mª López Muñoz wrote:
[...]
> > The problem now is that, although the eraseVal<...> expression is only
> > invoked (i.e. its nested ::type calculated) when strictly necessary
> > --this is what eval_if is used for--, its first argument is computed
> > *always*, regardless of what branch of the eval_if<...> expr is
> > finally selected:
>
> Is this sort of like saying that although a metafunction may not be
> invoked, it's arguments are always calculated?

No, not exactly. The thing to watch for is the rules for
implicit instantation of class templates in C++, which
roughly boil down to "a mention of a class template
specialization will implicit instantiate it only if
necessary". In particular, the mere mention of a class
template specialization

  X<...>

does not cause instantiation, but the expression

  X<...>::Y

causes the implicit instantiation of X<...> (although
it does not cause the instantiation of Y in case Y
is also a template specialization.) This applies
recursively to arguments of X. So, if you consider
again the eval_if expression

  mpl::eval_if<
    boost::is_same<
      iter,
      typename mpl::end<Seq>::type
>,
    mpl::identity<Seq>,
    eraseVal<
      typename mpl::erase<Seq,iter>::type,
      typename mpl::next<iter>::type
>
>

the mere mention of this expression causes the
implicit instantation of the following (and only
the following):

  mpl::end<Seq>
  mpl::erase<Seq,iter>
  mpl::next<iter>

So the problem does not actually have much to do with
metafunctions, we're talking pure C++ templates here.

> I'm probably still stuck in the procedural world, but my
> expectation was that the false branch would not be "taken"
> when the condition was false, but it's still murky to me
> what it means to "take" a branch, because where "taking"
> it entails some kind of template expansion along it. At
> least that's what I currently think.

Of course, the instantation of eval_if<...> (when you
add the ::type suffix) causes the instantiation of
more types, namely those in the true or false branch
depending on the condition, just as expected. But the problem
lies in the implicit instantiations caused by C++ rules
before eval_if is even invoked.

> > So, what we need if to add a layer of indirection so as to
> > not invoke the arguments to the recursive call to eraseVal
> > except when strictly necessary, like for instance as follows:
> >
> > template<typename Seq,typename T>
> > struct eraseVal
> > {
> > template<typename Iter>
> > struct eraseValRecurse:
> > eraseVal<typename mpl::erase<Seq,Iter>::type,T>
> > {};
> >
> > typedef typename mpl::find<Seq,T>::type iter;
> >
> > typedef typename mpl::eval_if<
> > boost::is_same<iter,typename mpl::end<Seq>::type>,
> > mpl::identity<Seq>,
> > eraseValRecurse<iter>
>
> And this works because we evaluate only iter in this last
> statement, not eraseValRecurse<iter> -- is that correct?

This works because the mere mention of eraseValRecurse<iter>
does not cause any implicit instantiation --it is only
when the "false" branch is taken that the expression
is instantatiated causing the instantiation in turn
of mpl::erase<...>: but in this context this latter
instantiation is correct and does not lead to infinite
recursions or invalid uses of MPL end iterators.

Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net