Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C++ (http://www.velocityreviews.com/forums/f39-c.html)
-   -   Specializing Perfect Forwarding Templates? (http://www.velocityreviews.com/forums/t745196-specializing-perfect-forwarding-templates.html)

Scott Meyers 03-16-2011 05:22 AM

Specializing Perfect Forwarding Templates?
 
Function templates can be partially or fully specialized, but is it
possible to specialize templates that perform perfect forwarding? My
sense is that it's not, because the special type deduction rule that
makes perfect forwarding work (the one that distinguishes lvalue and
rvalue arguments for function templates with a declared parameter type
of T&& -- 14.8.2.1/3 in the most recent draft C++0x) seems to apply only
to general templates. But maybe I'm overlooking something.

If I give the program below to VC10 and gcc 4.5, they behave as the
comments indicate. My attempt to partially specialize a perfect
forwarding template for pointer types fails. The code compiles and
runs, but the partial specialization is invoked only for rvalue pointer
types, never for lvalue pointer types.

I have two questions:
1. Am I correct that perfect forwarding templates cannot be specialized?
2. Does it ever make sense to want to?

Thanks,

Scott



#include <iostream>
#include <utility>

void f(std::string*&)
{
std::cout << "f(lref)\n";
}

void f(const std::string*&)
{
std::cout << "f(const lref)\n";
}

void f(std::string*&&)
{
std::cout << "f(rref)\n";
}

template<typename T>
void fwd(T&& param)
{
std::cout << "General forwarding template => ";
f(std::forward<T>(param));
}

template<typename T>
void fwd(T*&& param)
{
std::cout << "T*&& forwarding template => ";
f(std::forward<T*>(param));
}

int main()
{
std::string* ps;
const std::string *pcs;

fwd(ps); // calls general template
fwd(pcs); // calls general template
fwd((std::string*&&)ps); // calls specialized template
}



Alf P. Steinbach /Usenet 03-16-2011 07:20 AM

Re: Specializing Perfect Forwarding Templates?
 
* Scott Meyers, on 16.03.2011 06:22:
> Function templates can be partially or fully specialized,


Sorry, but at which version have partial specialization of function templates
been introduced, and can you give reference?

AFAIK what looks like a partial specialization is still just overloading.


> but is it possible to
> specialize templates that perform perfect forwarding? My sense is that it's not,
> because the special type deduction rule that makes perfect forwarding work (the
> one that distinguishes lvalue and rvalue arguments for function templates with a
> declared parameter type of T&& -- 14.8.2.1/3 in the most recent draft C++0x)
> seems to apply only to general templates. But maybe I'm overlooking something.
>
> If I give the program below to VC10 and gcc 4.5, they behave as the comments
> indicate. My attempt to partially specialize a perfect forwarding template for
> pointer types fails. The code compiles and runs, but the partial specialization
> is invoked only for rvalue pointer types, never for lvalue pointer types.
>
> I have two questions:
> 1. Am I correct that perfect forwarding templates cannot be specialized?
> 2. Does it ever make sense to want to?
>
> Thanks,
>
> Scott
>
>
>
> #include <iostream>
> #include <utility>
>
> void f(std::string*&)
> {
> std::cout << "f(lref)\n";
> }
>
> void f(const std::string*&)
> {
> std::cout << "f(const lref)\n";
> }
>
> void f(std::string*&&)
> {
> std::cout << "f(rref)\n";
> }
>
> template<typename T>
> void fwd(T&& param)
> {
> std::cout << "General forwarding template => ";
> f(std::forward<T>(param));
> }
>
> template<typename T>
> void fwd(T*&& param)
> {
> std::cout << "T*&& forwarding template => ";
> f(std::forward<T*>(param));
> }
>
> int main()
> {
> std::string* ps;
> const std::string *pcs;
>
> fwd(ps); // calls general template
> fwd(pcs); // calls general template
> fwd((std::string*&&)ps); // calls specialized template
> }


Well it's late in the day for me, but fiddling a little with your code produced
an apparent difference between the general template and the pointer arg template:


<code>
#include <iostream>
#include <utility>

void f(std::string*&)
{
std::cout << "f(lref)\n";
}

void f(const std::string*&)
{
std::cout << "f(const lref)\n";
}

void f(std::string*&&)
{
std::cout << "f(rref)\n";
}

template<typename T>
void fwd(T&& param)
{
std::cout << "General forwarding template => ";
f(std::forward<T>(param));
}

template<typename T>
void ptrfwd(T*&& param)
{
std::cout << "T*&& forwarding template => ";
f( std::forward<T*>(param));
}

template<typename T>
void fwd(T*&& param)
{ ptrfwd( std::forward<T*>(param) ); }

template<typename T>
void fwd(T*& param)
{ ptrfwd( param ); }

int main()
{
std::string* ps = 0;
const std::string *pcs = 0;

fwd(ps); // calls general template
fwd(pcs); // calls general template
fwd((std::string*&&)ps); // calls specialized template
}
</code>


<compilation>
C:\test> msvc x.cpp
x.cpp
x.cpp(39) : error C2664: 'ptrfwd' : cannot convert parameter 1 from 'std::string
*' to 'std::string *&&'
You cannot bind an lvalue to an rvalue reference
x.cpp(46) : see reference to function template instantiation 'void
fwd<std::string>(T *&)' being compiled
with
[
T=std::string
]
x.cpp(39) : error C2664: 'ptrfwd' : cannot convert parameter 1 from 'const
std::string *' to 'const std::string *&&'
You cannot bind an lvalue to an rvalue reference
x.cpp(47) : see reference to function template instantiation 'void
fwd<const std::string>(T *&)' being compiled
with
[
T=const std::string
]

C:\test> _
</compilation>


Why this result, cannot bind lvalue to rvalue-ref, when 'ptrwfd' is a general
template?


Cheers & hth.,

- Alf

--
blog at <url: http://alfps.wordpress.com>

Alf P. Steinbach /Usenet 03-16-2011 07:25 AM

Re: Specializing Perfect Forwarding Templates?
 
* Scott Meyers, on 16.03.2011 06:22:
>
> I have two questions:
> 1. Am I correct that perfect forwarding templates cannot be specialized?


Code below seems to work OK:


<code>
#include <iostream>
#include <utility>

void f(std::string*&)
{
std::cout << "f(lref)\n";
}

void f(const std::string*&)
{
std::cout << "f(const lref)\n";
}

void f(std::string*&&)
{
std::cout << "f(rref)\n";
}

template<typename T>
void fwd(T&& param)
{
std::cout << "General forwarding template => ";
f(std::forward<T>(param));
}

template<typename T>
void ptrfwd(T&& param)
{
//STATIC_ASSERT( T is pointer type )
std::cout << "T*&& forwarding template => ";
f( std::forward<T>(param));
}

template<typename T>
void fwd(T*&& param)
{ ptrfwd( std::forward<T*>(param) ); }

template<typename T>
void fwd(T*& param)
{ ptrfwd( param ); }

int main()
{
std::string* ps = 0;
const std::string *pcs = 0;

fwd(ps); // calls specialized template
fwd(pcs); // calls specialized template
fwd((std::string*&&)ps); // calls specialized template
}
</code>


Cheers & hth.,

- Alf (hey, why don't you mention me somewhere? I wanna be fammous! lol)

--
blog at <url: http://alfps.wordpress.com>

SG 03-16-2011 07:31 AM

Re: Specializing Perfect Forwarding Templates?
 
On 16 Mrz., 06:22, Scott Meyers wrote:
> Function templates can be partially or fully specialized,


Can they be? partially specialized? If so, this would be a new C++0x
feature. I take your word for it.

> but is it
> possible to specialize templates that perform perfect forwarding?


The code you provided does not contain a partial specialization of a
function template. You *overloaded* the function template fwd with
another "more specialized" one. That's different.

> void f(std::string*&)
> {
> * *std::cout << "f(lref)\n";
> }
>
> void f(const std::string*&)
> {
> * *std::cout << "f(const lref)\n";
> }
>
> void f(std::string*&&)
> {
> * *std::cout << "f(rref)\n";
> }
>
> template<typename T>
> void fwd(T&& param)
> {
> * *std::cout << "General forwarding template => ";
> * *f(std::forward<T>(param));
> }
>
> template<typename T>
> void fwd(T*&& param)
> {
> * *std::cout << "T*&& forwarding template => ";
> * *f(std::forward<T*>(param));
> }


From your use of std::forward I gather that you expect this template
to catch pointer lvalues and rvalues. But the deduction rule that
makes perfect forwarding possible is restricted to a function
parameter pattern "T&&" where T is a template parameter. Perfect
forwarding not only relies on the deduction rules but also on
reference collapsing. So, to make T&& an lvalue reference we just set
T to be an lvalue reference (or let the compiler deduce it to be an
lvalue reference). Reference collapsing makes T&& with T=U& into
T&&=U&. But T*&& can *never* be an lvalue reference regardless of what
T is. T*&& is *always* an rvalue reference.

SG

Scott Meyers 03-16-2011 08:00 AM

Re: Specializing Perfect Forwarding Templates?
 
On 3/16/2011 12:20 AM, Alf P. Steinbach /Usenet wrote:
> * Scott Meyers, on 16.03.2011 06:22:
>> Function templates can be partially or fully specialized,

>
> Sorry, but at which version have partial specialization of function
> templates been introduced, and can you give reference?
>
> AFAIK what looks like a partial specialization is still just overloading.


You're right, my bad. Please forgive my terminological sin.

> Code below seems to work OK:


[...]

> template<typename T>
> void fwd(T*&& param)
> { ptrfwd( std::forward<T*>(param) ); }
>
> template<typename T>
> void fwd(T*& param)
> { ptrfwd( param ); }


But notice how you have to overload to support both lvalues and rvalues.
This doesn't scale with multiple parameters, which is the motivation
for perfect forwarding: to be able to write one template that handles
both. If I could "partially specialize" a forwarding template for
pointer types, I'd be able to write a single template to handle all
pointer types, both lvalues and rvalues.

Scott

Scott Meyers 03-16-2011 08:05 AM

Re: Specializing Perfect Forwarding Templates?
 
On 3/16/2011 12:31 AM, SG wrote:
> Can they be? partially specialized? If so, this would be a new C++0x
> feature. I take your word for it.


No, as I pointed out in a response to a different post, I made the
common error of saying "specialization" when I meant overloading. I
hang my head in shame.

> From your use of std::forward I gather that you expect this template
> to catch pointer lvalues and rvalues. But the deduction rule that
> makes perfect forwarding possible is restricted to a function
> parameter pattern "T&&" where T is a template parameter.


Which is what I said I thought was the case. I was just wondering if I
had overlooked something somewhere. Even in draft form, it would not be
breaking new ground for the C++ standard to grant permission to do
something in one place that appears to not be permitted based on other
parts of the standard.

Scott

SG 03-16-2011 08:33 AM

Re: Specializing Perfect Forwarding Templates?
 
On 16 Mrz., 09:05, Scott Meyers wrote:
> SG wrote:
> > From your use of std::forward I gather that you expect this template
> > to catch pointer lvalues and rvalues. But the deduction rule that
> > makes perfect forwarding possible is restricted to a function
> > parameter pattern "T&&" where T is a template parameter.

>
> Which is what I said I thought was the case. *I was just wondering if I
> had overlooked something somewhere. *Even in draft form, it would not be
> breaking new ground for the C++ standard to grant permission to do
> something in one place that appears to not be permitted based on other
> parts of the standard.


You make it sound like if it was an artificial restriction that could
be easily overcome. But this is not the case. To get what you want we
would need some kind of constrained template and new overload
resolution rules based on these constraints. Example:

template<class T>
requires T=U* or T=U*& for some type U
void fwd(T&&);

SG

SG 03-16-2011 09:37 AM

Re: Specializing Perfect Forwarding Templates?
 
On 16 Mrz., 09:33, SG wrote:
> [...] To get what you want we
> would need some kind of constrained template and new overload
> resolution rules based on these constraints. Example:
>
> * template<class T>
> * requires T=U* or T=U*& for some type U
> * void fwd(T&&);


Or an alternative way to do perfect forwarding. You *did* point out
with your example that deduction w.r.t. "special, more restrictive
patters" a la T* is not orthogonal to the feature that allows us to
detect lvalues and rvalues. So, for a proper "orthogonal" approach we
could introduce a new kind of template parameter:

template<class T, qualifier Q>
void fwd(T* Q x);

where Q can be one of the 12=2*2*3 combinations you get by pairing
const/non-const, volatile/non-volatile and [no reference]/&/&&. The
deduction rule for Q would pick an lvalue reference for lvalues and an
rvalue reference for rvalues.

This way, we get rid of the issue you pointed out (w.r.t.
orthogonality), we can get rid of the funny/special deduction rule for
T&& (which is already known to cause trouble (*)) and also get a nice
way of expressing member function types

template<class R, class C, qualifier Q, class... P>
void (R (C::*memfunptr)(P...) Q);

without the need to overload std::bind for 12 different combinations
of qualifiers.

On the other hand, getting rid of the funny/special deduction rule for
the "T&&" pattern would force us to use two template parameters to do
perfect forwarding for just one function argument:

template<class...P, qualifiers...Q>
void outer(P Q... params)
{
inner(std::forward<P Q>(params)...);
}

I'm not sure about other implications, but it seems to be worth
thinking about it.

Cheers!
SG

itaj sherman 03-16-2011 11:10 AM

Re: Specializing Perfect Forwarding Templates?
 
On Mar 16, 7:22 am, Scott Meyers <NeverR...@aristeia.com> wrote:
> Function templates can be partially or fully specialized, but is it
> possible to specialize templates that perform perfect forwarding? My
> sense is that it's not, because the special type deduction rule that
> makes perfect forwarding work (the one that distinguishes lvalue and
> rvalue arguments for function templates with a declared parameter type
> of T&& -- 14.8.2.1/3 in the most recent draft C++0x) seems to apply only
> to general templates. But maybe I'm overlooking something.
>


What is supposed to happen if you wrap it into template class
specializations?

template< typename T >
class fwd_internal
{
public:
template<typename U>
static void call(U&& param)
{
std::cout << "General forwarding template => ";
f(std::forward<U>(param));
}
}

template< typename T* >
class fwd_internal
{
public:
template<typename U>
static void call(U&& param)
{
std::cout << "T*&& forwarding template => ";
f(std::forward<U>(param));
}
}

template<typename T>
void fwd(T&& param)
{
return fwd_internal<T>::call(std::forward<T>(param));
}

itaj

itaj sherman 03-16-2011 11:12 AM

Re: Specializing Perfect Forwarding Templates?
 
On Mar 16, 1:10*pm, itaj sherman <itajsher...@gmail.com> wrote:

oops specialization syntax:

>
> template< typename T* >
> class fwd_internal


template< typename T >
class fwd_internal< T* >

itaj



All times are GMT. The time now is 08:35 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.