Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Specializing Perfect Forwarding Templates?

Reply
Thread Tools

Specializing Perfect Forwarding Templates?

 
 
Scott Meyers
Guest
Posts: n/a
 
      03-16-2011
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
}


 
Reply With Quote
 
 
 
 
Alf P. Steinbach /Usenet
Guest
Posts: n/a
 
      03-16-2011
* 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>
 
Reply With Quote
 
 
 
 
Alf P. Steinbach /Usenet
Guest
Posts: n/a
 
      03-16-2011
* 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>
 
Reply With Quote
 
SG
Guest
Posts: n/a
 
      03-16-2011
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
 
Reply With Quote
 
Scott Meyers
Guest
Posts: n/a
 
      03-16-2011
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
 
Reply With Quote
 
Scott Meyers
Guest
Posts: n/a
 
      03-16-2011
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
 
Reply With Quote
 
SG
Guest
Posts: n/a
 
      03-16-2011
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
 
Reply With Quote
 
SG
Guest
Posts: n/a
 
      03-16-2011
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
 
Reply With Quote
 
itaj sherman
Guest
Posts: n/a
 
      03-16-2011
On Mar 16, 7:22 am, Scott Meyers <(E-Mail Removed)> 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
 
Reply With Quote
 
itaj sherman
Guest
Posts: n/a
 
      03-16-2011
On Mar 16, 1:10*pm, itaj sherman <(E-Mail Removed)> wrote:

oops specialization syntax:

>
> template< typename T* >
> class fwd_internal


template< typename T >
class fwd_internal< T* >

itaj

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
strange compile problem regarding const-ref v.s. perfect forwarding huili80@gmail.com C++ 3 10-29-2012 09:11 PM
Perfect Forwarding in Runtime (rvalue reference) dervih C++ 3 07-13-2012 02:03 PM
using std::function, std::bind, perfect forwarding Andrew Tomazos C++ 1 12-23-2011 10:19 AM
Perfect Forwarding + static_assert [C++0x] Scott Meyers C++ 6 12-05-2010 08:21 PM
Perfect function forwarding Alexis Nikichine Javascript 6 12-28-2005 02:39 PM



Advertisments