Hi forum,
First post here. I trying to learn myself C++ by writing small programs, which solves some of the problems at Project Euler (
http://projecteuler.net). I have previously solved a lot of those problems in Python, so the algorithms is not my challenge, but learning the language.
To aid me, I also have a copy of Stroustrup's "C++ Programming Language" by my side.
For some of those problems, a useful utility function is the ability to calculate the greatest common divisor [gcd(a, b)] between two integral types a and b, which is the greatest number which divides both a and b.
Since I might call gcd with a number of different integral types 16 bit, 32 bit, 64 bit or even larger custom types, it seems like a template function would be the right way to do this, so i have implemented the following template function using Euclid's algorithm
1 2 3 4 5 6 7 8 9 10 11 12 13
|
/**
* Returns the greatest common divisor using Euclid's algorithm
*/
template <class T>
T gcd(T a, T b){
T tmp;
while (b) {
tmp = a % b;
a = b;
b = tmp;
}
return a;
}
|
The code works for the test cases I have with different integral types and the template parameter T is resolved automatically, such that I can just call gcd(a, b) from the implementation.
However, Two things annoys me with this code:
1. There is no check that T is an integral type. Is there any method for doing that? Or will that always be resolved at compile time anyway because, e.g., non-integral types will not support the modulus operation in the template code? I would just like to make it more explicit in the template function declaration that it is only intended for use with integral types.
2. The presence of the tmp variable. In Python I can write
a, b = b, a % b
and avoid tmp altogether. Is there a C++ trick to avoid the explicit tmp here, or at least encapsulate it (perhaps in some other template)?
My next quest has been to make a variant of gcd, which operates on all integral values in one of the standard container types, like list and vector. The gcd of a sequence of numbers, can be done as a fold initiated with 0, using the previous gcd as a binary function, which is being called repeatedly. In Python I would use the
reduce
function to do that. As I understand the C++ equivalent to this is std::accumulate found in <numeric>. So I have coded the following template function, which seems to do the job
1 2 3 4 5 6 7 8
|
/**
* Returns the greatest common divisor for the sequence of
* integral values in a container
*/
template <class Container, class T>
T gcd(const Container& c){
return std::accumulate(c.begin(), c.end(), 0, gcd<T>);
}
|
Albeit this seems to work, there are a few things which annoy me here as well.
3. The class T parameter seems redundant as it out to be resolvable from the Container type. As a consequence it seems like I have to be very specific in specifying the template parameters, i.e.,
gcd<std::list<int>, int>(myList)
from the implementation, which is tedios as it ought to be resolvable from the myList type alone, I would guess. Is there any way to change the way the template function is declared to improve on that?
4. I first tried writing simply
gcd
in the accumulate function, but this was not resolvable, so I had to change to
gcd<T>
. It seems to me that the compiler should be capable of figuring out the template type from the context, that accumulate is being called with iterators to the container type.
5. Is this a good C++ implementation in the first place? I considered another signature, where the iterators were given as the arguments to gcd instead of a reference to the container, but this seemed more tedious to call from the implementation.
Thanks,
Kim