![]() |
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)? |
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 |
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. |
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. |
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? |
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. |
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 |
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 |
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? |
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 02:22 PM. |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.