Scott Meyers
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
the mere mention of this expression causes the
implicit instantation of the following (and only
the following):
mpl::end<Seq>
mpl::erase
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
struct eraseVal { template<typename Iter> struct eraseValRecurse: eraseVal ::type,T> {}; typedef typename mpl::find
::type iter; typedef typename mpl::eval_if< boost::is_same
, 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