|
Boost : |
Subject: Re: [boost] [offtopic] C++11 useful trick
From: Roland Bock (rbock_at_[hidden])
Date: 2012-07-02 04:12:54
On 2012-07-02 01:20, Eric Niebler wrote:
> I recently ran into a limitation in recursive functions that make use of
> decltype in trailing return types. I've come up with a work-around that
> I think is a little non-obvious, so I thought I'd share.
>
> The problem came up while trying to implementing something like a
> variadic back() function. The "obvious" implementation doesn't work:
>
> template<typename T>
> T back(T && t)
> {
> return t;
> }
>
> template<typename T, typename ...U>
> auto back(T && t, U &&... u)
> -> decltype(::back(static_cast<U &&>(u)...)) // ERROR
> {
> return ::back(static_cast<U &&>(u)...);
> }
>
> int main()
> {
> int i = ::back(1,2,3);
> }
>
> The problem occurs on the line marked "HERE". Trouble is, the variadic
> "back" function is not yet in scope until after the return type.
> However, we'd like to use it when specifying the return type.
>
> The solution uses a default function template parameter to make the
> ::back symbol dependent, thereby deferring it's lookup until phase 2,
> when the symbol is visible. Check it:
>
> struct S
> {
> template<typename T>
> static T back(T && t)
> {
> return t;
> }
>
> template<typename T, typename ...U, typename Impl = S>
> static auto back(T && t, U &&... u)
> -> decltype(Impl::back(static_cast<U &&>(u)...))
> {
> return Impl::back(static_cast<U &&>(u)...);
> }
> };
>
> int main()
> {
> int i = S::back(1,2,3);
> }
>
> The trick is the "typename Impl = S" followed by a (now dependent)
> invocation of Impl::back. Voila.
>
> Apologies if this is a known technique, and I'm spamming the list with
> old news. It's news to me.
>
Hmm, I would have done something like this:
// -----------------------------------------------------------------
namespace detail
{
template<typename T, typename ...U>
struct back_t
{
typedef typename back_t<U...>::type type;
};
template<typename T>
struct back_t<T>
{
typedef T type;
};
}
template<typename T>
T back(T && t)
{
return t;
}
template<typename T, typename ...U>
typename detail::back_t<U...>::type back(T && t, U &&... u)
{
return ::back(static_cast<U &&>(u)...);
}
int main()
{
int i = ::back(1,2,3);
}
// -----------------------------------------------------------------
The advantages I see are that
* nobody will be confused by the Impl parameter.
* no need to put back() into a struct
Do you see any drawbacks?
Regards,
Roland
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk