I have a template class A
template <unsigned int m> class A { public: A(int) {} };
Which has a constructor from int
. And I have an operation:
template<unsigned int m> A<m> operator+(const A<m>&, const A<m>&) { return A<m>(0); }
But when I call:
A<3> a(4); A<3> b = a + 5; A<3> c = 5 + a;
I would like int
to be implicitly converted to A, but compilers throws error.
Is there any elegant way to enable implicit conversion without using such solutions as:
a + A<m>(5)
operator+<3>(a, 5)
In Implicit type conversion, Python automatically converts one data type to another data type. This process doesn't need any user involvement. Let's see an example where Python promotes the conversion of the lower data type (integer) to the higher data type (float) to avoid data loss.
A template is a blueprint or formula for creating a generic class or a function. The library containers like iterators and algorithms are examples of generic programming and have been developed using template concept.
Implicit type conversion, also known as coercion or type juggling, is an automatic type conversion by the compiler. Some programming languages allow compilers to provide coercion; others require it.
An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.
The solution is already shown in this answer. Now, more about the problem...
The problem in your code is how overload resolution is performed. When a template function is considered for overload resolution the compiler will perform type deduction on the arguments and come up with a type substitution that matches the call or else it fails to apply that template, removes it from the set of potential candidates and continues over. The problem at this point is that type deduction only deduces exact matches (with possible extra const/volatile qualification). Because the matching is exact, the compiler will not use any conversion (again, other than cv).
The simplest example of this happens with std::max
and std::min
functions:
unsigned int i = 0; std::min( i, 10 ); // Error!
Type deduction will deduce the T in template <typename T> min( T const &, T const & )
to be unsigned
for the first argument but int
for the second they differ and the compiler will discard this template function.
The solution proposed in the answer is using a feature of the language that enables you to define a non-member friend function inside the class definition. The advantage with templates is that for every (different) instantiation of the template, the compiler will create a free non-template function at namespace level that has the signature obtained by substituting the real types of the instantiation in the friend declaration:
template <typename T> class test { friend test operator+( test const & lhs, test const & rhs ) { // [1] return test(); } } test<int> t; // [2]
In the example above, the compiler allows you to add the definition of the friend function inside the class scope at [1]. Then when you instantiate the template in [2], the compiler will generate a free function:
test<int> operator+( test<int> const & lhs, test<int> const & rhs ) { return test<int>(); }
The function is defined always, whether you use it or not (this differs to the template class member functions, that are instantiated on demand).
The magic here has multiple sides to it. The first part is that it generically you are defining non-template functions for each and all of the instantiated types, so you gain genericity and at the same time the advantage of overload resolution being able to use this function when the arguments are not perfect matches.
Because it is a non-template function, the compiler is able to call implicit conversions on both arguments, and you will get your expected behavior.
Additionally, a different type of magic goes on with lookup, as the function so defined can only be found by argument dependent lookup unless it is also declared at namespace level, which in our case cannot be done in a generic way. The implication of this might be good or bad, depending on how you want to consider it...
Because it can only be found by ADL it will not be considered unless at least one of the arguments is already of the desired type (i.e. it will never be used performing conversions to both arguments). The downside is that it is impossible to refer to the function unless you are actually calling it, and that means that you cannot obtain a function pointer.
(More on template friendship here, but note that in this particular case, all the other variants will fail to perform implicit conversions).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With