I was looking at this video CppCon 2018: Dan Saks “Making New Friends” please look here
I this talk, he refers to a (hypothetical) rational number class (which has numerator and denominator as private members and all of the computation revolves around this. I'm trying to implement whatever is mentioned in this talk just to understand templates well.
I that, Dan referred to a function to perform multiplication int*rational
//~ rational.h
template <typename> class rational;
template <typename T> std::ostream& operator<<(std::ostream&, const rational<T> &);
template <typename T> rational<T> operator*(const rational<T>&, const rational<T>&);
template <typename T>
class rational {
friend std::ostream& operator<<<T>(std::ostream&, const rational<T>&);
friend rational<T> operator*<T>(const rational<T>&, const rational<T>&);
public:
rational();
rational(T num);
rational(T num, T den);
rational(const rational &ro);
rational &operator+=(const rational &ro);
rational &operator*=(const rational &ro);
...
...
private:
T num, den;
};
// implements operator*
template <typename T>
rational<T> operator*(const rational<T> &lo, const rational<T> &ro) {
rational<T> result(ro);
result *= lo;
return result;
}
and then, we have main
//~ temp.cpp
#include <iostream>
#include "rational.h"
using namespace std;
int main(int argc, char *argv[]) {
rational<int> r1(1,3);
rational<int> r2(5,6);
cout << "r1=" << r1 << ", r2=" << r2 << endl;
rational<int> r3(3);
r3 = 3*r3;
cout << r3 << endl;
return 0;
}
But, when I build this, I get the error :
onkar@TITAN:~/cc/test$ make
g++ -g -Wno-unused-variable -Wno-unused-but-set-variable -std=c++11 -Wall -c test.cpp
test.cpp: In function ‘int main(int, char**)’:
test.cpp:14:8: error: no match for ‘operator*’ (operand types are ‘int’ and ‘rational<int>’)
14 | r3 = 3*r3;
| ~^~~
| | |
| | rational<int>
| int
In file included from test.cpp:2:
rational.h:58:13: note: candidate: ‘template<class T> rational<T> operator*(const rational<T>&, const rational<T>&)’
58 | rational<T> operator*(const rational<T> &lo, const rational<T> &ro) {
| ^~~~~~~~
rational.h:58:13: note: template argument deduction/substitution failed:
test.cpp:14:9: note: mismatched types ‘const rational<T>’ and ‘int’
14 | r3 = 3*r3;
| ^~
make: *** [Makefile:2: test] Error 1
onkar@TITAN:~/cc/test$
Now, I do understand this error (converting constructors take single argument and all) but question is :
how do I make sure r3 = 3*r3;
works (and I don't have to call (weird looking) r3 = rational<int>(3)*r3
which
anyways I can always do).
Implicit conversion won't be considered in template argument deduction. Given 3*r3
, template argument deduction for T
based on the 1st function argument (i.e. 3
) fails; the implicit conversion from int
to rational<int>
won't be considered.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
You can change operator*
to non-template, which doesn't have such issue and implicit conversion would work as you expected.
template <typename> class rational;
template <typename T> std::ostream& operator<<(std::ostream&, const rational<T> &);
template <typename T>
class rational {
friend std::ostream& operator<<<T>(std::ostream&, const rational<T>&);
friend rational<T> operator*(const rational<T> &lo, const rational<T> &ro) {
rational<T> result(ro);
result *= lo;
return result;
}
public:
rational();
rational(T num);
rational(T num, T den);
rational(const rational &ro);
rational &operator+=(const rational &ro);
rational &operator*=(const rational &ro);
...
...
private:
T num, den;
};