1. Examples of errors in non-member function templates
-
We said in Item 24 that for the Rational class, in order to allow all multiplication operations to pass, we define the operator*() function as a non-member function version (for details, you can look back again). But for templates, these rules may not hold
- For example, below we define both Rational and operator*() functions as templates. The code is as follows:
//下面与条款24都相同,只是改为了模板
template<typename T>
class Rational {
public:
Rational(const T& numerator = 0, const T& denominator = 1);
const T numerator()const;
const T denominator()const;
};
template<typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs)
{
}
- Now we write the following code, it cannot be compiled. E.g:
Rational<int> oneHalf(1, 2);
//在条款24中,Rational和operator*为非模板,此处代码可编译通过
//此处,Rational和operator*为模板,编译不通过
Rational<int> result = oneHalf * 2;
Analysis of the cause of the error
- The reason for the error is: the above "oneHalf*2" did not find a matching operator*() function, which led to an error
- the reason:
- The type of oneHalf is Rational<int>, so it matches the operator*() function template, and instantiates the template type T of the operator*() function template to int
- But when the second parameter is run, 2 is int, and the operator*() function template cannot infer the type of T based on int
- Therefore, the final compiler cannot find a suitable operator*() function and causes an error
- In non-template operator*(), operator*() can implicitly convert 2 to Rational objects (Rational provides implicit conversions, see Item 24 for details). But in the template, this implicit conversion is not allowed
- The deduction of template argument takes the decisive factor
2. Declare the template function as friend
- In "One", the template class and template function we used went wrong. One solution is to declare operator()* as a friend of the Rational template class.
- Below we have made two modifications, and finally implemented the wrong code
The first modification (compiled, but an error occurred during linking)
- We first declare operator()* as a friend of Rational . code show as below:
template<typename T> class Rational { public: //友元,函数声明 friend const Rational operator*(const Rational& lhs, const Rational& rhs); }; //函数定义 template<typename T> const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs); int main() { Rational<int> oneHalf(1, 2); //编译通过,但是链接时出错 Rational<int> result = oneHalf * 2; return 0; }
- Remarks: In the life of friend above, we did not add <T> after Rational, which is the same as Rational<T>, both are ok
- Reasons for compiling:
- When the object oneHalf is defined, the program will generate a definition of the Rational<int> class, so the class Rational<int> is instantiated
- After class Rational<int> is instantiated, the friend operator*() function is also automatically declared (Note: it is only declared but not defined)
- After the friend operator*() function is declared, the function is a function, not a template, so we can pass 2 to the function (and the parameter 2 can be implicitly type converted to convert 2 to Rational)
- So the above code can be compiled and passed, but an error occurred during linking (described below)
- The reason for the error when linking:
- Because when class Rational<int> is instantiated, friend only declares operator*(), but it is not defined, so the program reports an error
Second revision (correct version)
- The above code compiles and passes, but the link passes because the definition of operator*() cannot be found
- The way to solve the link error is to define this function at the same time as the declaration of the friend function. code show as below:
template<typename T> class Rational { public: //声明的时候,同时实现该函数(备注,该函数虽然在类中定义,但不属于成员函数) friend const Rational operator*(const Rational& lhs, const Rational& rhs) { return Rational(lhs.numerator()*rhs.numerator(), lhs.denominator()*lhs.denominator()); } };
- Here is another benefit of friend: Although we declare the function as friend, we do not have access to any non-public members of the class, so it is safer
3. Make the friend function call the auxiliary function (related to inline)
- We said in Item 30 that if a function is defined in a class, it will become inline, so the friend operator()* we defined in class above will also be called inline.
- In order to minimize the impact of inline, we can:
- We can do nothing in operator()* and move what we have done to another helper function
- This auxiliary function should be a template
-
code show as below:
-
In order to optimize the inline of operator*() in the class, we define an auxiliary function in which to complete the original function of operator*()
-
doMultiply() does not support mixed operations (for example, multiplying a Rational object and an int), but this function is only called by operator*(), and operator*() supports mixed operations, so the function is perfect
-
//声明
template<typename T> class Rational
//函数声明(定义在下)
template<typename T>
const Rational<T> doMultiply(const Rational<T>& lhs, const Rational<T>& rhs);
template<typename T>
class Rational {
public:
friend
const Rational operator*(const Rational& lhs, const Rational& rhs)
{
//在其中调用doMultiply(),可使inline最优化
return doMultiply(lhs, rhs);
}
};
//完成原本operator*()要完成的功能
template<typename T>
const Rational<T> doMultiply(const Rational<T>& lhs, const Rational<T>& rhs)
{
return Rational<T>(lhs.numerator()*rhs.numerator(),
lhs.denominator()*lhs.denominator());
}
Four, summary
- When we write a class template, and the "template related" functions it provides support "implicit type conversion of all parameters", please define those functions as "friend functions inside the class template"