Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C++ (http://www.velocityreviews.com/forums/f39-c.html)
-   -   Overloading vs C++ 0x variadic templates (http://www.velocityreviews.com/forums/t730855-overloading-vs-c-0x-variadic-templates.html)

er ci 08-13-2010 08:11 PM

Overloading vs C++ 0x variadic templates
 
Hello,

I have a function g, overloaded on # of arguments, which forwards to
f:

template<typename F>
struct foo{

foo(){}

void g(){ this->f(); }
template<typename T0> void g(T0& a){ this->f( a ); }
template<typename T0,typename T1> void g(T0& a,T1& b){ this->f( a,
b ); }
// etc. a certain number of times. Usually this is done with a pp
macro.
private:
F f;
};

f need not be defined for each overload, only those that are actually
called.

I was hoping, naively as it turns out, that in C++0x, the above could
be replaced by

template<typename... Args>
void g(Args... args){ this->f(args); }

but the compiler says "parameter pack not expanded with".

Any suggestion, please (besides learning C++0x, thank you very much,
that's what I'm trying to do here)?



Howard Hinnant 08-13-2010 09:44 PM

Re: Overloading vs C++ 0x variadic templates
 
On Aug 13, 4:11*pm, er ci <er.ci.2...@gmail.com> wrote:
> Hello,
>
> I have a function g, overloaded on # of arguments, which forwards to
> f:
>
> template<typename F>
> struct foo{
>
> * *foo(){}
>
> * *void g(){ this->f(); }
> * *template<typename T0> void g(T0& a){ this->f( a ); }
> * *template<typename T0,typename T1> void g(T0& a,T1& b){ this->f( a,
> b ); }
> * *// etc. a certain number of times. Usually this is done with a pp
> macro.
> * private:
> * *F f;
>
> };
>
> f need not be defined for each overload, only those that are actually
> called.
>
> I was hoping, naively as it turns out, that in C++0x, the above could
> be replaced by
>
> template<typename... Args>
> void g(Args... args){ this->f(args); }
>
> but the compiler says "parameter pack not expanded with".
>
> Any suggestion, please (besides learning C++0x, thank you very much,
> that's what I'm trying to do here)?


This ought to do it:


#include <utility>

template<typename F>
struct foo{
foo(){}

template<typename... Args>
void g(Args&&... args)
{ this->f(std::forward<Args>(args)...); }
private:
F f;
};

Explanation: You were lacking "args...". Also you're lacking
"perfect forwarding" which is what the "Args&&" and "forward<Args>" is
all about. In a nutshell, the perfect forwarding will duplicate in
the call to f(), the lvalue/rvalue-ness and cv-quals present in the
call to g(). This is important for move semantics: temporaries sent
to g() can be moved from within f().

-Howard

bx12 08-14-2010 03:25 AM

Re: Overloading vs C++ 0x variadic templates
 

> This ought to do it:
>
> #include <utility>
>
> template<typename F>
> struct foo{
> * *foo(){}
>
> * *template<typename... Args>
> * * void g(Args&&... args)
> * * * * { this->f(std::forward<Args>(args)...); }
> private:
> * *F f;
>
> };
>


Thanks, very useful.

As it is though, calling g with an rvalue, say g(5), will not work
unless f specifically caters to rvalues. So, I'd like to add that
twist to the initial problem. The old fashioned way it would be

template<typename F>
struct foo{

foo(){}

// n = 0
void g(){ this->f(); }

// n = 1
template<typename T0> void g(T0& a){ this->f<T0>( a ); }
template<typename T0> void g(T0 const& a){ this->f<T0
const>( a ); }

// n = 2
template<typename T0,typename T1> void g(T0& a,T1& b){
this->f( a, b );
}
template<typename T0,typename T1> void g(T0& a,T1 const& b){
this->f<T0, T1 const>( a,b );
}
// and <T0 const, T1> and <T0 const, T1 const>

// n = 3,...,N Usually done with a pp macro.
private:
F f;

};


The point, here, is that f need not be overloaded on lvalue/const.
lvalue is enough:

template<typename T0> f(T0& );
template<typename T0,typename T1> f(T0&,T1&);
etc.

Now, how would Howard's code have to be modified to achieve this with C
++0x?

Thanks.



er 08-14-2010 06:24 AM

Re: Overloading vs C++ 0x variadic templates
 
Thanks, very useful.

As it is though, calling g with an rvalue, say g(5), will not work
unless f specifically caters to rvalues. So, I'd like to add that
twist to the initial problem. The old fashioned way it would be

template<typename F>
struct foo{

foo(){}

// n = 0
void g(){ this->f(); }

// n = 1
template<typename T0> void g(T0& a){ this->f<T0>( a ); }
template<typename T0> void g(T0 const& a){ this->f<T0
const>( a ); }

// n = 2
template<typename T0,typename T1> void g(T0& a,T1& b){
this->f( a, b );
}
template<typename T0,typename T1> void g(T0& a,T1 const& b){
this->f<T0, T1 const>( a,b );
}
// and <T0 const, T1> and <T0 const, T1 const>

// n = 3,...,N Usually done with a pp macro.
private:
F f;

};

The point, here, is that f need not be overloaded on lvalue/const.
lvalue is enough:

template<typename T0> f(T0& );
template<typename T0,typename T1> f(T0&,T1&);
etc.

Now, how would Howard's code have to be modified to achieve this with
C
++0x?

Thanks.


Juha Nieminen 08-14-2010 08:11 AM

Re: Overloading vs C++ 0x variadic templates
 
Howard Hinnant <howard.hinnant@gmail.com> wrote:
> #include <utility>
>
> template<typename F>
> struct foo{
> foo(){}
>
> template<typename... Args>
> void g(Args&&... args)
> { this->f(std::forward<Args>(args)...); }
> private:
> F f;
> };
>
> Explanation: You were lacking "args...". Also you're lacking
> "perfect forwarding" which is what the "Args&&" and "forward<Args>" is
> all about. In a nutshell, the perfect forwarding will duplicate in
> the call to f(), the lvalue/rvalue-ness and cv-quals present in the
> call to g(). This is important for move semantics: temporaries sent
> to g() can be moved from within f().


In an older thread someone claimed that the rvalue reference
operator && will not, after all, work with lvalues in C++0x. If
that is so, then I don't understand how the above code can work
(except if *all* the parameters happen to be rvalues). Or is
"&&..." somehow a special case?

Luc Danton 08-14-2010 09:56 AM

Re: Overloading vs C++ 0x variadic templates
 
On 14/08/2010 10:11, Juha Nieminen wrote:
> In an older thread someone claimed that the rvalue reference
> operator&& will not, after all, work with lvalues in C++0x. If
> that is so, then I don't understand how the above code can work
> (except if *all* the parameters happen to be rvalues). Or is
> "&&..." somehow a special case?


int&& i = 5; // Binds ok

int&& j = i;
// Note that the name 'i' is an lvalue
// So this one doesn't bind

This also applies for function parameters:
void func(int&& i);

func(5); // Ok
int i = 42;
func(i); // Not ok

There *is* a special rule, and that's in a template deduction context
(not variadic templates):
template<typename T>
void func(T&& t); // Will eat anything you throw at it...

int lvalue = 4;
int const& clvalue = lvalue;

func(lvalue);
// Ok, T&& deduced to int& &&, collapses to int&
// func<int&> called

func(clvalue);
// Ok, T&& deduced to int const& &&,
// collapses to const int&
// func<const int &> called

func(std::move(lvalue))
// Ok, T&& deduced to int &&, no collapsing needed
// func<int> called
// Note that int&& && would collapse to int&&

So there really are two flavours of 'T&&': for rvalue references (which
is quite straightforward), and for perfect-forwarding, which is designed
to be used idiomatically and is powerful, esp. when combined with
std::forward, but actually needs some extra rules to make it work. I
guess we should really tell the two apart, e.g. calling 'int&& i' a
rvalue reference and 'template<typename T> ... T&& t' a perfect
reference or something.

If you're wondering, no practical use was found for const rvalues so
they got left behind; don't use them.

Howard Hinnant 08-14-2010 02:44 PM

Re: Overloading vs C++ 0x variadic templates
 
On Aug 14, 2:24*am, er <er.ci.2...@gmail.com> wrote:
> Thanks, very useful.
>
> As it is though, calling g with an rvalue, say g(5), will not work
> unless f specifically caters to rvalues.


I'm probably misunderstanding your statement above. The way I'm
reading that statement it can be reworded like so:

Calling g with an rvalue, say g(5), won't work, unless it does
work.

Sorry, I'm really not meaning to be obstructive. I'm truly not
understanding your problem domain yet.

> So, I'd like to add that
> twist to the initial problem. The old fashioned way it would be
>
> template<typename F>
> struct foo{
>
> * *foo(){}
>
> * *// n = 0
> * *void g(){ this->f(); }
>
> * *// n = 1
> * *template<typename T0> void g(T0& a){ this->f<T0>( a ); }
> * *template<typename T0> void g(T0 const& a){ this->f<T0
> const>( a ); }
>
> * *// n = 2
> * *template<typename T0,typename T1> void g(T0& a,T1& b){
> * * * * this->f( a, b );
> * *}
> * *template<typename T0,typename T1> void g(T0& a,T1 const& b){
> * * *this->f<T0, T1 const>( a,b );
> * *}
> * *// and <T0 const, T1> and <T0 const, T1 const>
>
> * *// n = 3,...,N Usually done with a pp macro.
> * private:
> * *F f;
>
> };
>
> The point, here, is that f need not be overloaded on lvalue/const.
> lvalue is enough:


In the above formulation, g(5) can be called (binds to the const&
overload). Indeed, it is this 2^n "explosion" that perfect forwarding
is intended to avoid. Reference:

http://www.open-std.org/jtc1/sc22/wg...2/n1385.htm#s3

> template<typename T0> f(T0& );
> template<typename T0,typename T1> f(T0&,T1&);
> etc.
>
> Now, how would Howard's code have to be modified to achieve this with
> C
> ++0x?


If you would like to disallow rvalue arguments to g() you can do this:

template<typename F>
struct foo{
foo(){}

template<typename... Args>
void g(Args&... args)
{ this->f(args...); }
private:
F f;
};

But this isn't equivalent to your T0&, const T1& implementation as it
disallows g(5).

-Howard

Howard Hinnant 08-14-2010 02:51 PM

Re: Overloading vs C++ 0x variadic templates
 
On Aug 14, 5:56*am, Luc Danton <lucdan...@free.fr> wrote:
> On 14/08/2010 10:11, Juha Nieminen wrote:
>
> > * *In an older thread someone claimed that the rvalue reference
> > operator&& *will not, after all, work with lvalues in C++0x. If
> > that is so, then I don't understand how the above code can work
> > (except if *all* the parameters happen to be rvalues). Or is
> > "&&..." somehow a special case?

>
> int&& i = 5; // Binds ok
>
> int&& j = i;
> // Note that the name 'i' is an lvalue
> // So this one doesn't bind
>
> This also applies for function parameters:
> void func(int&& i);
>
> func(5); // Ok
> int i = 42;
> func(i); // Not ok
>
> There *is* a special rule, and that's in a template deduction context
> (not variadic templates):
> template<typename T>
> void func(T&& t); // Will eat anything you throw at it...
>
> int lvalue = 4;
> int const& clvalue = lvalue;
>
> func(lvalue);
> // Ok, T&& deduced to int& &&, collapses to int&
> // func<int&> called
>
> func(clvalue);
> // Ok, T&& deduced to int const& &&,
> // collapses to const int&
> // func<const int &> called
>
> func(std::move(lvalue))
> // Ok, T&& deduced to int &&, no collapsing needed
> // func<int> called
> // Note that int&& && would collapse to int&&
>
> So there really are two flavours of 'T&&': for rvalue references (which
> is quite straightforward), and for perfect-forwarding, which is designed
> to be used idiomatically and is powerful, esp. when combined with
> std::forward, but actually needs some extra rules to make it work. I
> guess we should really tell the two apart, e.g. calling 'int&& i' a
> rvalue reference and 'template<typename T> ... T&& t' a perfect
> reference or something.


If it helps anyone, here is a little more discussion of perfect
fowarding:

http://www.open-std.org/jtc1/sc22/wg...ect_Forwarding

> If you're wondering, no practical use was found for const rvalues so
> they got left behind; don't use them.


Actually a few use cases for const A&& are beginning to emerge. Here
is one example:

http://www.open-std.org/jtc1/sc22/wg...fects.html#688

-Howard

er 08-14-2010 03:26 PM

Re: Overloading vs C++ 0x variadic templates
 
> In the above formulation, g(5) can be called (binds to the const&
> overload). *Indeed, it is this 2^n "explosion" that perfect forwarding
> is intended to avoid. *Reference:


Yes, I'm aware that perfect forwarding caters to that, but here the
problem is with f, or rather the f--g interaction, not g alone. So
let me rephrase:

template<typename U> f(U&);

// n = 1
template<typename U> f(U&); // U = T or T const
template<typename T> g(T& t){ f( t ); }
template<typename T> g(T const & t){ f( t ); }
// etc. for n = 1,...,N

template<Args...> g_0x(Args&&...args)
{ f(std::forward<Args>( args )...); }
template<Args...> h_0x(Args&&...args)
{ f<Args...>( std::forward<Args>( args )... ); }


int main(){

g( 1 ); // ok
g_0x( 1 ); // invalid initialization of non-const ref from
temporary
h_0x( 1 ); // no matching function for call to f(int)
return 0;
}

What should be done to g/h _0x's definition for this to compile, and
if nothing can be done, what should be done to that of f?

er 08-14-2010 03:30 PM

Re: Overloading vs C++ 0x variadic templates
 
> template<typename U> f(U&);
>
> // n = 1
> template<typename U> f(U&); // U = T or T const


PS: The redundancy above is non-intentional. There should be only one
definition for f.



All times are GMT. The time now is 04:46 PM.

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