Я пишу некоторую библиотеку шаблонов (скажем, библиотеку линейной алгебры) и получил следующую сложную ошибку, которую я могу воспроизвести только на GCC (Clang и VC ++ работают как положено).
Библиотекасостоит из общих типов шаблонов, таких как
template<class C, int N>
class Vector;
template<class C, int N, int M = N>
class Matrix;
с некоторыми реализациями по умолчанию, которые не используются. Также есть набор классов интерфейса, подобных этим
template<class C, int N>
struct VecMulInterface
{
Vector<C, N> operator*(const Vector<C, N>& v)
{
return static_cast<Matrix<C, N, N>*>(this)->mul_impl(v);
}
};
template<class C, int N>
struct ScalMulInterface
{
Matrix<C, N, N> operator*(const C& c)
{
return static_cast<Matrix<C, N, N>*>(this)->mul_impl(c);
}
};
и соответствующим поставщикам реализаций
template<class C, int N>
struct MulImpl
{
public:
Vector<C, N> mul_impl(const Vector<C, N>& v) const
{
return {}; // imagine some logic here
}
Matrix<C, N, N> mul_impl(const C& c) const
{
return {}; // imagine some logic here
}
};
Наконец, у меня есть специализации шаблонов, которые используют все, что указано выше:
template<class C>
class Vector<C, 2>
{
public:
C items[2];
// ...
};
template<class C>
class Matrix<C, 2, 2>:
public VecMulInterface<C, 2>,
public ScalMulInterface<C, 2>,
public MulImpl<C, 2>
{
public:
C items[4];
// ...
};
Я пытаюсь использовать их следующим образом:
Matrix<int, 2, 2> m;
Vector<int, 2> v1, v2;
v2 = m * v1; // <- error
Теперь GCC выдает ошибку «ошибка: запрос на член 'operator *' является неоднозначным». Но! Если я изменяю строку с ошибкой так, что я использую перегруженную функцию вместо 'operator *'
v2 = m.mul_impl( v1 ); // <- NO error
ИЛИ, если вместо наследования от соответствующих интерфейсов я помещаю операторы вне класса, как это:
template<class C, int N>
Vector<C, N> operator*(const Matrix<C, N, N>& m, const Vector<C, N>& v)
{
return m.mul_impl(v);
}
template<class C, int N>
Matrix<C, N, N> operator*(const Matrix<C, N, N>& m, const C& c)
{
return m.mul_impl(c);
}
все работает просто отлично. (VC ++ и Clang, кажется, работают правильно во всех случаях)
Может ли кто-нибудь объяснить причину такого поведения? Это ошибка компилятора, или я перешел к «неопределенному поведению» где-то в коде?