## Implicit type conversions with templates

Suppose you want to write a template class X that must allow for operations like X = X + Y or X = Y + X, where Y is any type like int, float or other class type. How you want to start with that? Normally you would write operator+ as a free function and add it as a friend to X. Lets assume we want Y to be an int. Below code seems to solve this scenario:

template<typename T> class X { public: int n; X(int rn) : n(rn) {} template<class U> friend X<U> operator+(const X<U>& lhs, const X<U>& rhs); }; template<typename U> X<U> operator+(const X<U>& lhs, const X<U>& rhs) { X<U> ret(0); ret.n = lhs.n + rhs.n; return ret; } int main() { X<int> a = 1; X<int> b = 2; X<int> c = a + b; c = c + 1; c = 1 + c; std::cout << "X : " << c.n << std::endl; }

unfortunately this will result in errors (compilation using clang):

source_file.cpp:25:7: error: invalid operands to binary expression ('X<int>' and 'int') c = c + 1; ~ ^ ~ source_file.cpp:15:7: note: candidate template ignored: could not match 'X<type-parameter-0-0>' against 'int' X<U>& operator+(const X<U>& lhs, const X<U>& rhs) { ^ ...

the problem is that template operator+ is not considered as a valid overload. In case of function templates, compiler will perform type deduction on the arguments and come up with a template parameter types that will match the call or else it will remove such function from list of potential candidates. Whats important here is that no implicit conversions will be done. So to make above code compile we would have to change class usage to:

c = c + X<int>(1); c = X<int>(1) + c;

this way, there is no need for implicit conversion using X(int rn) constructor. But this is not what we want. The solution is to use a language feature that enables you to define a non-member friend function inside template class definition, as in the following example:

template<typename T> class X { public: int n; X(int rn) : n(rn) {} friend X operator+(const X& lhs, const X& rhs) { X ret(0); ret.n = lhs.n + rhs.n; return ret; } }; int main() { X<int> a = 1; X<int> b = 2; X<int> c = a + b; c = c + 1; c = 1 + c; std::cout << "X : " << c.n << std::endl; }

Now this compiles and outputs X: 5. Lets analyse what `friend X operator+(const X& lhs, const X& rhs)`

means. First, X is the same as X<T>, due to class name injection (within the scope of a class template X with template parameters P1, P2, … the name of that template (X) can be equivalent to the template-id X<P1, P2, …>). Second thing is that compiler will generate for any used template parameter type a free non-template function at namespace level (outside of class X) which will be used during normal function call. Whats important for us here is that compiler will perform implicit conversions on its arguments. Such functions will be generated always, even if not used by given class instantiation. Below is example of such compiler generated free function:

X<int> operator+(const X<int>& lhs, const X<int>& rhs) { X<int> ret(0); ret.n = lhs.n + rhs.n; return ret; }

This is not a new feature in C++, I suppose its c++98 wording is easier to understand.

C++98 [temp.friend]p5: When a function is defined in a friend function declaration in a class template, the function is defined at each instantiation of the class template. The function is defined even if it is never used. C++11 [temp.friend]p4: When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used. The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.

## Leave a Reply