Boost logo

Boost :

Subject: Re: [boost] [offtopic] C++11 useful trick
From: Eric Niebler (eric_at_[hidden])
Date: 2012-07-02 01:03:17


On 7/1/2012 9:53 PM, Steve Ramsey wrote:
> On Jul 1, 2012, at 4:20 PM, 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.
>
> Isn’t it the case that the back function without the U parameter pack has the same function signature as the back function with an empty U parameter pack, which would be the source of your troubles in the first case? The struct-based case simply ensures that the two backs will always have differing signatures since there will always be at least two template parameters for the second function, but this isn’t due to any special struct-related property. Admittedly, I’m still working to understand variadic templates, so I might be off-base here...

You're off base. Although the variadic back *could* be called with 1
argument, overload resolution would prefer the non-variadic function
over the variadic one. But that's irrelevant in this case. look at the
invocation: ::back(1,2,3). That can *only* call the variadic one. It
peels off one element and then tries to call ::back(2,3). That also
could only possibly call the variadic one. However, it can't for the
simple reason that the compiler doesn't know about it until after the
trailing return type is parsed. But by making the invocation of back in
the trailing return type dependent on a template argument, it forces
name lookup to happen later, when the template arguments are known.

HTH,

-- 
Eric Niebler
BoostPro Computing
http://www.boostpro.com

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