Let's start with a simple add method for class number
:
class number {
int num;
public:
number(int num = 0): num(num) {}
operator int() const { return num; }
};
number add(number t1, number t2) {
return t1 + t2;
}
int main() {
auto result1 = add(1, 2); // auto-casting, works fine
}
Now we want to turn number
into a template class:
template<class T>
class number {
T num;
public:
number(T num = 0): num(num) {}
operator T() const { return num; }
};
template<class T>
number<T> add(number<T> t1, number<T> t2) {
return t1 + t2;
}
Trying to call add
the same as we called the simple non-templated one, based (theoretically!) on CTAD:
int main() {
number a = 3; // works, using CTAD
// auto result1 = add(1, 2); // <== what we wish for, but can't find method
// auto result2 = add(a, 2); // this also doesn't work, no ADL here :(
auto result3 = add<int>(1, 2); // this works, but is not what we wish
}
Note that if add
was a friend function, calling it with one of the parameters being a number
would work, based on ADL:
template<class T>
class number {
T num;
public:
number(T num = 0): num(num) {}
operator T() const { return num; }
friend number add(number t1, number t2) {
return t1 + t2;
}
};
int main() {
number a = 3; // works, using CTAD
auto result1 = add(a, 2); // works, based on ADL
// auto result2 = add(1, 2); // still doesn't work, no ADL here :(
}
Any suggestion how to allow the template class to behave similarly to the non-template, having auto casting when calling add?
EDIT:
This question was edited based on posted comments. It should be emphasized that for a generic function like add
having such an auto casting might be a wrong idea, but suppose that the method is very specific, something like doSomethingWithNumbers
.
I think the answer is simple. In the first case, with the free function template, what kicks in first is overloading resolution and function template argument deduction. As the compiler has no way to detect
T
from theint
passed as argument (clang says:candidate template ignored: could not match 'number<type-parameter-0-0>' against 'int'
), the overloading resolution fails and the program is ill formed.When the function is defined as
friend
, it's a non-template function. The compiler created it when instantiating the class (fornumber<int>
; first line inmain
). Now, when it finds it (using ADL), the parameter types are already set (both arenumber<int>
because it comes fromnumber<int>
instantiation), and what's left is to decide on how to convert the passed argument fromint
tonumber<int>
, where the implicit conversion (by the matching c-tor) is used. No CTAD here either.A similar (but not exactly the same) case is discussed by Scott Meyers in Effective C++ (3rd edition), Item 46: Define non-member functions inside templates when type conversions are desired.
Edit: So to answer the question, function template argument deduction and implicit type conversion for the arguments can't be mixed. Choose one. (Which is what Meyers explains in the mentioned item.)