Library design Q : overloads v/s default args

Hope this is not a silly question. The C++ Standard library does not use default arguments. Instead it provides an additional overload. Anybody know why ? For e.g. It provides the following two overloads for accumulate : template<class Itr, class T> accumulate(Itr first, Itr last, T init); template<class Itr, class T, class BinOp> accumulate(Itr first, Itr last, T init, BinOp op); instead of: template<class Itr, class T, class BinOp> accumulate(Itr first, Itr last, T init, BinOp op = std::plus<T>() ); I am wondering if there is any reason to avoid default arguments in general purpose libraries ? - Roshan

Roshan wrote:
The C++ Standard library does not use default arguments. Instead it provides an additional overload. Anybody know why ?
For e.g.
It provides the following two overloads for accumulate :
template<class Itr, class T> accumulate(Itr first, Itr last, T init);
template<class Itr, class T, class BinOp> accumulate(Itr first, Itr last, T init, BinOp op);
instead of:
template<class Itr, class T, class BinOp> accumulate(Itr first, Itr last, T init, BinOp op = std::plus<T>() );
N3092 (C++0x FDC), 17.6.4.5 Member functions [member.functions] says :
An implementation may declare additional non-virtual member function signatures within a class: — by adding arguments with default values to a member function signature;190 [ Note: An implementation may not add arguments with default values to virtual, global, or non-member functions. -end note ] — by replacing a member function signature with default values by two or more member function signatures with equivalent behavior; and...
So an implementation of the Standard Library is allowed to use default arguments, instead of having additional overloads, as long as they behave the same.
I am wondering if there is any reason to avoid default arguments in general purpose libraries ?
The additional overloads /might/ perform slightly better in some cases, because they would avoid constructing default values, and passing them as argument. HTH, Niels -- Niels Dekker http://www.xs4all.nl/~nd/dekkerware Scientific programmer at LKEB, Leiden University Medical Center

Zitat von Roshan <roshan_naik@yahoo.com>:
template<class Itr, class T, class BinOp> accumulate(Itr first, Itr last, T init, BinOp op = std::plus<T>() );
I am wondering if there is any reason to avoid default arguments in general purpose libraries ?
this case won't compile, template arguments are not deduced from default arguments.

At Sun, 20 Jun 2010 12:17:43 +0200, Stefan Strasser wrote:
Zitat von Roshan <roshan_naik@yahoo.com>:
template<class Itr, class T, class BinOp> accumulate(Itr first, Itr last, T init, BinOp op = std::plus<T>() );
I am wondering if there is any reason to avoid default arguments in general purpose libraries ?
this case won't compile, template arguments are not deduced from default arguments.
A problem, incidentally, that can be solved using Boost.Parameter. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

David Abrahams wrote:
Stefan Strasser wrote:
I am wondering if there is any reason to avoid default arguments in general purpose libraries ?
this case won't compile, template arguments are not deduced from default arguments.
A problem, incidentally, that can be solved using Boost.Parameter.
It depends, especially if "perfect forwarding" (see [1] why I use "...") of some parameters is also required. The attached file is automatically generated, and has a bunch of overloads for the syev template at the end, to handle parameters that benefit from "perfect forwarding" as well as handling the "Default workspace-type (optimal)". If I remember correctly, Rutger had reviewed Boost.Parameter first, before implementing the current solution. As some of the generated files were around ~10 MB in size, I recently wondered whether the techniques from Boost.Move (i.e. it's implementation of boost::forward) could help with this problem. However, I wasn't clever enough for this, so I gave up and instead tried to reduce the number of parameters that are considered for "perfect forwarding". I argued that only parameters corresponding to "vector arguments with general strides" or "matrix arguments" will likely be used with adapters, so that parameters corresponding to "normal vector arguments" could be excluded from "perfect forwarding" by default. Then I invented the name VectorView for "vector arguments with general strides", applied my changes and rejoiced when I noticed that the largest file was now ~100 kB in size. But if there is a better solution, I'm curious to learn about it. Regards, Thomas [1] It's not really about "perfect forwarding", but about capturing the non-const rvalue references. This is required for handling calls using adapters like "lapack::syev(bindings::lower(A), w);".

At Sun, 20 Jun 2010 16:21:14 +0200, Thomas Klimpel wrote:
[1 <text/plain; us-ascii (quoted-printable)>] David Abrahams wrote:
Stefan Strasser wrote:
I am wondering if there is any reason to avoid default arguments in general purpose libraries ?
this case won't compile, template arguments are not deduced from default arguments.
A problem, incidentally, that can be solved using Boost.Parameter.
It depends, especially if "perfect forwarding" (see [1] why I use "...") of some parameters is also required.
Boost.Parameter does a very good approximation of perfect forwarding, generating T&/T const& parameters for all out- and in/out- parameters. It does erase rvalue-ness, but that's pretty much a given with C++03 that AFAICT nobody knows how to avoid.
[1] It's not really about "perfect forwarding", but about capturing the non-const rvalue references. This is required for handling calls using adapters like "lapack::syev(bindings::lower(A), w);".
I'm just curious, Have you thought about using const rvalues as return types in these situations? If you consider the adapter itself to be immutable and the target sequence to be a separate object (which in reality it is), it actually all makes sense. And a T const return type will bind nicely to a generalized U& argument. Cheers, -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On 6/20/2010 11:00 AM, David Abrahams wrote:
At Sun, 20 Jun 2010 16:21:14 +0200, Thomas Klimpel wrote:
[1] It's not really about "perfect forwarding", but about capturing the non-const rvalue references. This is required for handling calls using adapters like "lapack::syev(bindings::lower(A), w);".
I'm just curious, Have you thought about using const rvalues as return types in these situations? If you consider the adapter itself to be immutable and the target sequence to be a separate object (which in reality it is), it actually all makes sense. And a T const return type will bind nicely to a generalized U& argument.
Interesting to hear you recommending this practice. I use it extensively in Proto but have felt like a bad citizen for doing it because I've heard that practice proscribed by various experts and committee members. -- Eric Niebler BoostPro Computing http://www.boostpro.com

At Sun, 20 Jun 2010 11:18:13 -0400, Eric Niebler wrote:
On 6/20/2010 11:00 AM, David Abrahams wrote:
At Sun, 20 Jun 2010 16:21:14 +0200, Thomas Klimpel wrote:
[1] It's not really about "perfect forwarding", but about capturing the non-const rvalue references. This is required for handling calls using adapters like "lapack::syev(bindings::lower(A), w);".
I'm just curious, Have you thought about using const rvalues as return types in these situations? If you consider the adapter itself to be immutable and the target sequence to be a separate object (which in reality it is), it actually all makes sense. And a T const return type will bind nicely to a generalized U& argument.
Interesting to hear you recommending this practice. I use it extensively in Proto but have felt like a bad citizen for doing it because I've heard that practice proscribed by various experts and committee members.
To heck with the “experts!” I remember discussing that possibility with you years ago, so you shoulnd't be surprised :-) I think at the time you were leery because you also wanted your code to work with 3rd-party functions that return non-const rvalues. loving-playing-the-populist-demagogue-ly y'rs, -- Dave Abrahams BoostPro Computing http://www.boostpro.com

David Abrahams wrote:
I'm just curious, Have you thought about using const rvalues as return types in these situations? If you consider the adapter itself to be immutable and the target sequence to be a separate object (which in reality it is), it actually all makes sense. And a T const return type will bind nicely to a generalized U& argument.
It's unbelievable, this really works. I had first deactivated the "perfect forwarding" code generation and tried to reproduce the compile errors. This proved quite tricky, as MSVC seems to have no problems binding temporaries against lvalue references (I don't know whether the deduced type will be const or not) , but gcc-4.4 and gcc-4.5 failed to compile as expected. Then I changed the return types from the wrappers to "T const" and gcc-4.4 and gcc-4.5 no longer failed to compile. So now I will have to post an update to the ublas mailing list...
Boost.Parameter does a very good approximation of perfect forwarding, generating T&/T const& parameters for all out- and in/out- parameters. It does erase rvalue-ness, but that's pretty much a given with C++03 that AFAICT nobody knows how to avoid.
In fact, Rutgers code doesn't erase rvalue-ness as well, it was just me misinterpreting things. So what he does is really just a "perfect forwarding" emulation, just as Boost.Parameter does. However, I wonder whether Boost.Parameter uses the preprocessor to implicitly generate a similar amount of code as Rutgers solution explicitly generates. In that case, I would wonder whether it would be possible to selectively turn off the T const& overload for parameters that don't need it (so that we could still use the "workaround" mentioned above). Thanks again. Regards, Thomas

At Sun, 20 Jun 2010 18:46:16 +0200, Thomas Klimpel wrote:
Boost.Parameter does a very good approximation of perfect forwarding, generating T&/T const& parameters for all out- and in/out- parameters. It does erase rvalue-ness, but that's pretty much a given with C++03 that AFAICT nobody knows how to avoid.
In fact, Rutgers code doesn't erase rvalue-ness as well,
I think you mean *does*, right?
it was just me misinterpreting things. So what he does is really just a "perfect forwarding" emulation, just as Boost.Parameter does. However, I wonder whether Boost.Parameter uses the preprocessor to implicitly generate a similar amount of code as Rutgers solution explicitly generates.
Well, Boost.Parameter has the advantage of knowing which parameters need to be passed by non-const reference, so unless all your parameters are “out” or “in_out” parameters, it probably does better than Rutger's solution.
In that case, I would wonder whether it would be possible to selectively turn off the T const& overload for parameters that don't need it (so that we could still use the "workaround" mentioned above).
Why would you want to turn that one off, as opposed to the T& one (as Boost.Parameter does)? Seems kinda crazy. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

David Abrahams wrote:
In fact, Rutgers code doesn't erase rvalue-ness as well,
I think you mean *does*, right?
Right.
it was just me misinterpreting things. So what he does is really just a "perfect forwarding" emulation, just as Boost.Parameter does. However, I wonder whether Boost.Parameter uses the preprocessor to implicitly generate a similar amount of code as Rutgers solution explicitly generates.
Well, Boost.Parameter has the advantage of knowing which parameters need to be passed by non-const reference, so unless all your parameters are “out” or “in_out” parameters, it probably does better than Rutger's solution.
Even so I can well imagine that Boost.Parameter might work better in certain cases than Rutger's solution, I can't imagine how Boost.Parameter could have more information about the code than the code generator.
In that case, I would wonder whether it would be possible to selectively turn off the T const& overload for parameters that don't need it (so that we could still use the "workaround" mentioned above).
Why would you want to turn that one off, as opposed to the T& one (as Boost.Parameter does)? Seems kinda crazy.
Well, perhaps the old fortran interfaces with their unbelievable long lists of input and output parameters are crazy. Below is one overload for ggevx, but the code generator has generated 2^5=32 of these. The function has "only" 12 template parameters, but 15 "out" or "in_out" parameters. So if there is no way to tell Boost.Parameter for which of these "in_out" parameters the "T const&" overload can be omitted, it will be forced to generate 2^15 = 32768 overloads. // // Overloaded function for ggevx. Its overload differs for // * MatrixA& // * MatrixB& // * MatrixVL& // * MatrixVR& // * User-defined workspace // template< typename MatrixA, typename MatrixB, typename VectorALPHAR, typename VectorALPHAI, typename VectorBETA, typename MatrixVL, typename MatrixVR, typename VectorLSCALE, typename VectorRSCALE, typename VectorRCONDE, typename VectorRCONDV, typename Workspace > inline typename boost::enable_if< detail::is_workspace< Workspace >, std::ptrdiff_t >::type ggevx( const char balanc, const char jobvl, const char jobvr, const char sense, MatrixA& a, MatrixB& b, VectorALPHAR& alphar, VectorALPHAI& alphai, VectorBETA& beta, MatrixVL& vl, MatrixVR& vr, fortran_int_t& ilo, fortran_int_t& ihi, VectorLSCALE& lscale, VectorRSCALE& rscale, typename remove_imaginary< typename bindings::value_type< MatrixA >::type >::type& abnrm, typename remove_imaginary< typename bindings::value_type< MatrixA >::type >::type& bbnrm, VectorRCONDE& rconde, VectorRCONDV& rcondv, Workspace work ) { return ggevx_impl< typename bindings::value_type< MatrixA >::type >::invoke( balanc, jobvl, jobvr, sense, a, b, alphar, alphai, beta, vl, vr, ilo, ihi, lscale, rscale, abnrm, bbnrm, rconde, rcondv, work ); } Regards, Thomas

At Mon, 21 Jun 2010 16:58:52 +0200, Thomas Klimpel wrote:
David Abrahams wrote:
In fact, Rutgers code doesn't erase rvalue-ness as well,
I think you mean *does*, right?
Right.
it was just me misinterpreting things. So what he does is really just a "perfect forwarding" emulation, just as Boost.Parameter does. However, I wonder whether Boost.Parameter uses the preprocessor to implicitly generate a similar amount of code as Rutgers solution explicitly generates.
Well, Boost.Parameter has the advantage of knowing which parameters need to be passed by non-const reference, so unless all your parameters are “out” or “in_out” parameters, it probably does better than Rutger's solution.
Even so I can well imagine that Boost.Parameter might work better in certain cases than Rutger's solution, I can't imagine how Boost.Parameter could have more information about the code than the code generator.
Oh, I didn't realize there was a code generator involved, so maybe not. All I meant was that you tell Boost.Parameter which parameters need to be passable by non-const reference.
In that case, I would wonder whether it would be possible to selectively turn off the T const& overload for parameters that don't need it (so that we could still use the "workaround" mentioned above).
Why would you want to turn that one off, as opposed to the T& one (as Boost.Parameter does)? Seems kinda crazy.
Well, perhaps the old fortran interfaces with their unbelievable long lists of input and output parameters are crazy. Below is one overload for ggevx, but the code generator has generated 2^5=32 of these. The function has "only" 12 template parameters, but 15 "out" or "in_out" parameters. So if there is no way to tell Boost.Parameter for which of these "in_out" parameters the "T const&" overload can be omitted, it will be forced to generate 2^15 = 32768 overloads.
Ouch. Are all those matrices and vectors actually open for modification, though? I'd bet you dollars to donuts that most of the parameters are never modified, and we're just looking at a very naive translation of a const-less FORTRAN or C interface.
// // Overloaded function for ggevx. Its overload differs for // * MatrixA& // * MatrixB& // * MatrixVL& // * MatrixVR& // * User-defined workspace // template< typename MatrixA, typename MatrixB, typename VectorALPHAR, typename VectorALPHAI, typename VectorBETA, typename MatrixVL, typename MatrixVR, typename VectorLSCALE, typename VectorRSCALE, typename VectorRCONDE, typename VectorRCONDV, typename Workspace > inline typename boost::enable_if< detail::is_workspace< Workspace >, std::ptrdiff_t >::type ggevx( const char balanc, const char jobvl, const char jobvr, const char sense, MatrixA& a, MatrixB& b, VectorALPHAR& alphar, VectorALPHAI& alphai, VectorBETA& beta, MatrixVL& vl, MatrixVR& vr, fortran_int_t& ilo, fortran_int_t& ihi, VectorLSCALE& lscale, VectorRSCALE& rscale, typename remove_imaginary< typename bindings::value_type< MatrixA >::type >::type& abnrm, typename remove_imaginary< typename bindings::value_type< MatrixA >::type >::type& bbnrm, VectorRCONDE& rconde, VectorRCONDV& rcondv, Workspace work ) { return ggevx_impl< typename bindings::value_type< MatrixA >::type >::invoke( balanc, jobvl, jobvr, sense, a, b, alphar, alphai, beta, vl, vr, ilo, ihi, lscale, rscale, abnrm, bbnrm, rconde, rcondv, work ); }
Regards, Thomas _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Dave Abrahams BoostPro Computing http://www.boostpro.com

David Abrahams wrote:
Ouch. Are all those matrices and vectors actually open for modification, though? I'd bet you dollars to donuts that most of the parameters are never modified, and we're just looking at a very naive translation of a const-less FORTRAN or C interface.
The code generator actually reads the documentation of the blas/lapack routine to find out which parameters are input and which are output parameters. In the case of "ggevx", there are really 15 output parameters: <http://www.netlib.org/lapack/explore-html/sggevx.f.html> Regards, Thomas

At Mon, 21 Jun 2010 19:40:58 +0200, Thomas Klimpel wrote:
In the case of "ggevx", there are really 15 output parameters: <http://www.netlib.org/lapack/explore-html/sggevx.f.html>
Bleah! -- Dave Abrahams BoostPro Computing http://www.boostpro.com
participants (6)
-
David Abrahams
-
Eric Niebler
-
Niels Dekker - address until 2010-10-10
-
Roshan
-
Stefan Strasser
-
Thomas Klimpel