I encountered an issue using Apple LLVM compiler (shipped with XCode 4.5.2), while running correctly with GCC. More importantly than a debate on compiler issues (I think GCC is right though), this raises the question about the resolution order of template specialization when overloaded operators are concerned [1].
Consider a simple matrix class template <class T> class matrix_t, with a traits defining the result type of multiplication (with scalar, matrices or vectors). This would be something like the following code:
template <class T, class U, class Enable = void>
struct matrix_multiplication_traits {
//typedef typename boost::numeric::conversion_traits<T,U>::supertype type;
typedef double type;
};
template <class T, int N>
struct vectorN {
std::vector<T> vect;
vectorN() : vect(N) { for(int i = 0; i < N; i++) vect[i] = i; }
};
template <class T>
class matrix_t {
std::vector<T> vec;
public:
matrix_t<T> operator*(matrix_t<T> const& r) const {
std::cout << "this_type operator*(this_type const& r) const" << std::endl;
return r;
}
template <class U>
matrix_t<typename matrix_multiplication_traits<T, U>::type>
operator*(U const &u) const {
std::cout << "different_type operator*(U const &u) const" << std::endl;
return matrix_t<typename matrix_multiplication_traits<T, U>::type>();
}
};
Consider also a specialization of the operator* for the vectorN:
template <class T, class U, int N>
//vectorN<typename matrix_multiplication_traits<T,U>::type, N>
vectorN<double, N>
operator*(matrix_t<T> const&m, vectorN<U, N> const&v)
{
std::cout << "vectorN operator*(matrix, vectorN)" << std::endl;
//return vectorN<typename matrix_multiplication_traits<T,U>::type, N>();
return vectorN<double, N>();
}
and consider a simple test program:
int main(int argc, const char * argv[])
{
matrix_t<double> test;
vectorN<double, 10> my_vector;
test * my_vector; // problematic line
return 0;
}
The "problematic line" runs the globally defined operator*(matrix_t<T> const&, vectorN<U, N> const&) on GCC and template <class U> matrix_t<T>::operator*(U const&) const on LLVM. So it is like the matrix_t<T>::operator*(U const&) is catching all template specialization lookups. A simple "fix" would be to move the global operator* into the matrix class.
I first thought it was a problem in the traits class, that was maybe too complex or erroneous (SFINAE). But even simplifying the traits or completely disabling it (as in the paste code) produces the error. I then thought it was an order problem (like in Herb Shutter's article), but moving the global operator* between matrix_t declaration and definition does not change things.
Here is the question
Of course, the real problem is that template <class U> matrix_t::operator*(U const&) const is too general, but:
- is this kind of problem something covered by the standard?
- does an operator overload defined inside a class have priority over an operator overload defined globally?
- (more like a vocabulary issue) what is the qualification of
operator*(matrix_t<T> const&, vectorN<U, N> const&)? template overload operator specialization? Is this more a template specialization or an overloaded function? What is the base definition for that? Since it is intrinsically an overloaded operator, I am a bit lost.
[1] I have read Herb Shutter advices on template specialization order.