Boost logo

Boost :

Subject: Re: [boost] [result_of] Allow result_of to work with C++11 lambdas
From: Jeffrey Lee Hellrung, Jr. (jeffrey.hellrung_at_[hidden])
Date: 2013-04-21 20:50:22


On Sun, Apr 21, 2013 at 7:48 AM, Daniel Walker <daniel.j.walker_at_[hidden]>wrote:

>
> On Apr 21, 2013, at 3:04 AM, "Vicente J. Botet Escriba" <
> vicente.botet_at_[hidden]> wrote:
>
> > Le 20/04/13 23:25, Nathan Crookston a écrit :
> >> Hi Daniel,
> >>
> >> Daniel Walker wrote:
> >>
> >>> Nathan Crookston wrote:
> >>>> I just wanted to ping on this; see if someone's had time to review the
> >>>> patch. I failed to mention that the same parts of the full test suite
> >>>> passes on VC10 before and after the patch, so there's some assurance
> that
> >>>> it's correct.
> >>> OK, I finally had a chance to look at the patch. The implementation is
> >>> fine, but the configuration, documentation and tests allow/imply that
> >>> BOOST_RESULT_OF_USE_TR1_WITH_DECLTYPE_FALLBACK is enabled by default on
> >>> certain compilers. I believe we should not stealthily change the
> default
> >>> behavior. It seems to me that the migration path we adopted years ago
> >>> should remain in place: the default behavior of boost:result_of will
> only
> >>> change if your compile supports N3276, in which case you will get the
> full
> >>> decltype implementation.
> >>>
> >>> I think it would be best to introduce this functionality as a third
> >>> option; i.e. one that the user opts into by explicitly defining
> >>> BOOST_RESULT_OF_USE_TR1_WITH_DECLTYPE_FALLBACK. (That's a good name
> for the
> >>> macro, BTW.) If you could update the patch, I'd be glad to apply it.
> >>>
> >> Thanks for looking over the patch! I'd be happy to make the change, if
> >> that means getting the functionality into boost. However, I think that
> the
> >> decltype fallback should be enabled at least for those compilers which
> have
> >> both a lacking decltype implementation and C++11 lambdas. This doesn't
> >> change the migration path, of course -- as N3276 is implemented in more
> >> compilers, more compilers will use the straight (standards conforming)
> >> decltype implementation.
> >>
> >> I don't think there are typical situations where someone using lambda
> >> functions would *not* want boost::result_of to work for it. However,
> I'll
> >> submit a patch updated as you request in a few days, unless I and/or
> others
> >> can help change your mind. ;)
> >>
> > Could, defining BOOST_RESULT_OF_USE_TR1_WITH_DECLTYPE_FALLBACK by
> default on these compilers, result on a regression on these compilers?
> >
> > If not it should be done as it allows to take care of more cases.
> >
> > Do we know of some regression cases?
>
> It's not a matter of regressions but user expectations. boost::result_of's
> documentation has said for a long time now that if decltype is not used
> then TR1 is. It doesn't seem like a good idea to me to introduce a new
> default scenario, a hybrid, which is a little hard to explain.

Fair enough, but I don't think the fact that it's difficult to explain
should, in and of itself, preclude its introduction.

> Everything is easier if there is one switch between two standards for the
> default behavior, namely, support for N3276.
>

Well, not universally. Those who want result_of to work with C++11 lambda's
on compilers which do no support N3276 will find this new default scenario
easier, I imagine.

However, there will also be regressions, i.e. valid user code that will no
> longer compile if we change the default. An obvious one is user code that
> is conditioned on the current two modes. If we change the default to a new
> third mode, then such code will suddenly fail to compile in the default
> case. For example:
>
> struct functor {
> template<class T>
> T operator()(T);
> };
>
> template<class T>
> struct S {
> #ifdef BOOST_RESULT_OF_USE_DECLTYPE
> typedef boost::result_of<functor(T)>::type type;
> #endif
>
> #ifdef BOOST_RESULT_OF_USE_TR1
> struct TR1functor {
> template <class> struct result;
> template<class F, class U>
> struct result<F(U)> {
> typedef T type;
> };
>
> template<class U>
> U operator()(U);
> };
>
> typedef typename boost::result_of<TR1functor(T)>::type type;
> #endif
> };
>
> typedef S<int>::type type; // error if the default changes
>

Is this excised from actual code or a purely contrived example? Is it
documented that we guarantee that defined( USE_DECLTYPE ) ^ defined(
USE_TR1 ) ? If so, this seems like a brittle and overspecified guarantee to
begin with. Anyways, at worst, can't we just define USE_TR1 for the hybrid
result_of anyway?

There will be more subtle errors as well. The following will compile by
> default on compilers with insufficient decltype support, since TR1 is used
> by default, but it will fail if we change the default to the hybrid mode.
>
> struct functor {
> int operator()();
> };
>
> BOOST_STATIC_ASSERT((
> boost::is_same<boost::result_of<functor()>::type, void>::value
> ));
>
> This is due to a dark corner of TR1: the result and result_type members
> are consulted in non-nullary functors but nullary functors always generate
> void. (See the section on known difference between boost::result_of and TR1
> in the documentation.)
>

The rationale for this always confused me, with the end result (it seems)
that it makes TR1 result_of basically unusable for nullary function calls.
In any case, again, at worst, we can retain this (dare I say,
questionable?) behavior for nullary function calls for hybrid result_of.

- Jeff


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk