Boost logo

Boost :

Subject: Re: [boost] [Fit] Formal review / Kris
From: Paul Fultz II (pfultz2_at_[hidden])
Date: 2016-03-21 17:52:03


On Monday, March 21, 2016 at 4:30:16 PM UTC-5, Louis Dionne wrote:
>
> Paul Fultz II wrote
> > On Monday, March 21, 2016 at 8:34:16 AM UTC-5, Louis Dionne wrote:
> >>
> >> [...]
> >>
> >> I'm not sure I understand what you mean. I mean, it is expected that
> >> the function is never called when the first one could be called, since
> >> it's overload _linearly_, right?
> >>
> >
> > Yes, but another way to achieve that is to use a ranked tag-dispatching.
> > Like this:
> >
> > template
> > <int N>
> > struct rank : rank
> > <N-1>
> > {};
> >
> > template<>
> > struct rank<0>
> > {};
> >
> > // Call F
> > template
> > <class... Ts>
> > auto foo_impl(Ts&&... xs, rank<1>)
> > -> decltype(std::declval
> > <F>
> > ()(std::forward
> > <Ts>
> > (xs)...));
> >
> > // Call G
> > template
> > <class... Ts>
> > auto foo_impl(Ts&&... xs, rank<0>)
> > -> decltype(std::declval
> > <G>
> > ()(std::forward
> > <Ts>
> > (xs)...));
> >
> > template
> > <class... Ts>
> > auto foo_impl(Ts&&... xs)
> > -> decltype(foo_impl(std::forward
> > <Ts>
> > (xs)..., rank<1>{}));
> >
> > So `G` will not be called if `F` is callable, however, the compiler will
> > instantiate both `F` and `G` with this implementation. I used to use an
> > implementation like this, but it is more costly at compile time.
> > Furthermore,
> > lazy instantiation is nice feature as well, especially when using
> > constexpr,
> > as the body of a constexpr function is always instantiated during
> > substitution.
> >
>
> I'm very surprised to learn that the compiler will instantiate both F and
> G
> in the code you posted above. Can't the compiler determine that the first
> `foo_impl` should be chosen if `decltype(std::declval<F>()(...))`does not
> SFINAE out because rank<1> is a better match than rank<0>, and this
> without ever looking at `std::declval<G>()(...)`?
>

I would think it would more complicated for the compiler to build a
structure
to know that.
 

>
> Anyway, IIUC, `hana::overload_linearly` has this problem, so I'll change
> it.
> Thanks for the heads up.
>

After looking at it closer, I don't think Hana has that problem, because you
never invoke the second function when the function is picked. You do
something
like this instead:

template<int N>
struct rank : rank<N-1>
{};

template<>
struct rank<0>
{};

// pick F
template<class... Ts,
    // Constraint on this function for when F is not callable
    class=decltype(std::declval<F>()(std::forward<Ts>(xs)...))>
F foo_which(rank<1>) { return F(); }

// pick G, there is no constraint
template<class... Ts>
G foo_which(rank<0>) { return G(); }

template<class... Ts>
decltype(auto) foo(Ts&&... xs)
{
    return foo_which<Ts&&...>(rank<1>{})(std::forward<Ts>(xs)...);
}

So the call to `G` will never get instantiated until its picked.
 

>
> Louis
>
>
>
>
> --
> View this message in context:
> http://boost.2283326.n4.nabble.com/Fit-Formal-review-Kris-tp4684694p4684785.html
> Sent from the Boost - Dev mailing list archive at Nabble.com.
>
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>


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