Reputation:
I always thought that with templated functions, no implicit conversion may happen and the types of the arguments must exactly match the templated types of the parameters, or else the template argument deduction will fail.
Well, it seems I was mistaken.
Consider the following snippet:
#include <iostream>
#include <type_traits>
template <class T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
class myInt
{
T val;
public:
myInt(T val) : val(val) {}
template <class U, typename = typename std::enable_if<std::is_integral<U>::value>::type>
friend bool operator < (myInt<T> const &t, myInt<U> const &u)
{
return t.val < u.val;
}
};
int main()
{
myInt<int> i = 5;
myInt<int> j = 6;
// std::cout << (i < 6) << std::endl; // gives out compiler errors
std::cout << (5 < j) << std::endl; // works!
}
I'm not sure why does the second std::cout << (5 < j)
work. Implicit conversion must have happened here, which I thought was prohibited. And I'm even less sure why doesn't the std::cout << (i < 6)
work if the former one does!
Edit: Compiler errors for std::cout << (i < 6)
:
test.cpp: In function ‘int main()’:
test.cpp:23:21: error: no match for ‘operator<’ (operand types are ‘myInt<int>’ and ‘int’)
std::cout << (i < 6) << std::endl; // gives out compiler errors
^
test.cpp:23:21: note: candidate is:
test.cpp:12:17: note: template<class U, class> bool operator<(const myInt<int>&, const myInt<T>&)
friend bool operator < (myInt<T> const &t, myInt<U> const &u)
^
test.cpp:12:17: note: template argument deduction/substitution failed:
test.cpp:23:23: note: mismatched types ‘const myInt<T>’ and ‘int’
std::cout << (i < 6) << std::endl; // gives out compiler errors
Upvotes: 0
Views: 77
Reputation: 27133
The reason this is "assymetrical" is that there is a fundamental difference between U
and T
in this:
template <class U, typename =
typename std::enable_if<std::is_integral<U>::value>::type>
friend bool operator < (myInt<T> const &t, myInt<U> const &u);
operator<
is a template, and U
is a parameter to this template. But T
is not a parameter to this template. T
is actually a parameter to a different template, myInt
, and is therefore fixed (to T=int
) in this context.
In effect, you have this:
template <class U, /*SFINAE stuff*/>
friend bool operator < (myInt<int> const &t, myInt<U> const &u);
Given (5 < j)
it does know how to convert 5
to a myInt<int> const &
for t
. And it can easily pass j
(a myInt<int>
) to myInt<U> const &u
by inferring U
= `int.
But (j<6)
won't work, because it doesn't know how to pass 6
to a template<class U> ..... myInt<U> const &u
. In particular, how can it guess which U
will create a suitable myInt<U>
with the necessary constructor? Perhaps myInt<int>
has a string
constructor, and myInt<string>
has an int
constructor! The compiler can't know.
I think you could instead do this:
/* *NOT* a template */
friend bool operator < (myInt<T> const &t, myInt<T> const &u);
T instead of U ~~~~~~~~~^
Upvotes: 1