Daniel Krügler wrote:
Jonathan Turkanis wrote:
Daniel Krügler wrote:
Jonathan Turkanis wrote:
Daniel Krügler wrote:
2) The function template
template <typename IntType> IntType lcm(IntType n, IntType m);
should be defined as
template <typename IntType> IntType lcm(IntType n, boost::call_traits<IntType>::param_type m);
which makes sense in this case because m is not modified internally (in contrast to n)
I think lcm should be symmetrical.
1) Wow, not so much of details of reasonings, please ;-))
Because the semantics are symmetrical. The above asymmetry wouldn't be acceptable for max or +, would it?
No, because they would use
template <typename IntType> IntType lcm(const IntTyp& n, const IntTyp& m);
as shown in my previous posting.
My point was: even if the assymetrical version were more efficient, the interface would be unacceptable.
How much performance improvement do you expect, and in what cases?
I expect performance differences in case of (most probably dynamically growing) arbitrary-precision integer types, as described in my original posting. As explained in detail by referencing the boost docs it is an explicit intention of the current design.
I guess I missed your reference to the docs.
Following my reasoning we currently have one unnecessary copy of one the 2nd argument in lcm and both of my proposals do not have this deficiency. I think we don't have to discuss about cases where the arguments are native integral types, but we have to consider something like std::integer, see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1744.pdf
Okay. Pass by value should be okay for built-in integral types, then.
2) OK, the request of symmetry can lead to several conclusions:
If we argue that the interface should not reflect the implementation, the most reasonable signature would be something like
template <typename IntType> IntType lcm(const IntTyp& n, const IntTyp& m);
(We cannot use the optimal version
template <typename IntType> IntType lcm(boost::call_traits<IntType>::param_type n, boost::call_traits<IntType>::param_type m);
because that would prevent argument deduction)
Otherway around we could argue, that we want to use advantages of a special copy-optimization technique. This is an implementation-driven interface, but there are cases, where proposers (I am not one) have argued that the advantages outweight its disadvantages.
Okay, what are you arguing, then?
Interuption of the sentences does not make sense here. I think the answer follows from the complete paragraph and I do answer below.
I put my comment between two of your paragraphs. You're the one who interrupted the sentences ;-)
If I try to compare these to approaches not too strongly subjectively (its impossible, I know, but I'll try ;-))), than I would say, that an implementation-driven interface should be only used where necessary, but not otherwise. This lead to my proposal to use call-by-value only for the first, not for the second argument of lcm.
I don't understand, **why** you think, that the interface must/should be symmetric:
1) Thinking not implementation-driven, than
template <typename IntType> IntType lcm(const IntTyp& n, const IntTyp& m);
would be right thing for general IntTypes (see std::max/min interface). I would agree to that proposal
Are you saying this is always better than pass by value, or just sometimes?
As **one** general solution this would be best, yes. Currently there is only one general solution, so I am discussing about that.
Call by value would be probably somewhat better for native integral types (This is the reason for call_traits). This fact was the reason for my original proposal of the asymmetric version.
Thinking in **one** general solution, I propose either the asymmetric version or the version using reference arguments as both arguments, whatever you like. I only wanted to show a direction which to my opinion is better than the current state, I don't say that my proposal is the best of the set of all possible solutions.
If one prefers finer granulation one could of course replace the single general solution by SFINAE-overloading techniques to handle cheap-copyable types by others and if any one would prefer that, I would applaud to that.
Why do we need SFINAE? I can just define overloads for built-in intergal types, and a templated version that uses const references.
2) Thinking implementation-driven, than
template <typename IntType> IntType lcm(IntType n, boost::call_traits<IntType>::param_type m);
must be acceptable and **even more reasonable** than the currently existing
template <typename IntType> IntType lcm(IntType n, boost::call_traits<IntType>::param_type m);
interface.
They look the same to me.
Sorry, I made a mistake here. My (asymmetric) proposal is:
template <typename IntType> IntType lcm(IntType n, boost::call_traits<IntType>::param_type m);
and the current state is:
template <typename IntType> IntType lcm(IntType n, IntType m);
Okay.
Daniel
Jonathan