我不明白这个链接错误

时间:2013-06-13 02:29:08

标签: c++ c++11

我不明白这个链接错误。 我有2个班级:

#include "Vector3.h"
#include "Quaternion.h"

template<typename T>
class Point3 final
{
public:
   constexpr Point3(const Vector3<T>& vec)
   : x(vec.x), y(vec.y), z(vec.z)
   {}

   constexpr operator const Vector3<T>() const
   {
      // It is the equivalent of Vector3 = Point3 - Origin
      return Vector3<T>(x, y, z);
   }

  constexpr operator Vector3<T>() const
  {
     // It is the equivalent of Vector3 = Point3 - Origin
     return Vector3<T>(x, y, z);
  }

  T x = T(0);
  T y = T(0);
  T z = T(0);

  friend Vector3<T>;
  friend Quaternion<T>;

  friend Vector3<T> operator*( const Quaternion<T>& lhs, const Vector3<T>& rhs);
  friend Vector3<T> operator*( Vector3<T> lhs, const Vector3<T>& rhs);
};

typedef Point3<Float32> Point3f;

template<typename T>
class Vector3 final
{
public:

  constexpr Vector3()
  {}

  constexpr Vector3(T _x, T _y, T _z)
  : x(_x), y(_y), z(_z)
  {}

  T x = T(0);
  T y = T(0);
  T z = T(0);

};

typedef Vector3<Float32> Vector3f;

我还有一个Quaternion类,细节是无关紧要的,但是这个类有一个非成员运算符*:

 template<typename T>
 Vector3<T> operator*( const Quaternion<T>& lhs, const Vector3<T>& rhs)
 {
    // nVidia SDK implementation
    Vector3<T> qvec(lhs.x, lhs.y, lhs.z);
    Vector3<T> uv = cross(qvec, rhs) * T(2.0) * lhs.w;    //uv = qvec ^ v;
    Vector3<T> uuv = cross(qvec, uv) * T(2.0);    //uuv = qvec ^ uv;
    return rhs + uv + uuv;
 }

现在这些行会产生链接错误,但为什么呢?

Math::Point3<Float32> pt = -Math::Point3<Float32>::UNIT_Z;
Math::Vector3<Float32> vec = orientation_*pt; // link error here (orientation is a Quaternion<Float32>)
//Math::Vector3<Float32> vec = orientation_*Math::Vector3<Float32>(pt); // this solve the link error.

这是链接错误

Undefined symbols for architecture x86_64:
  Math::operator*(Math::Quaternion<float> const&, Math::Vector3<float> const&), referenced from:
  GfxObject::Procedural::BoxGenerator::addToTriangleBuffer(GfxObject::Procedural::TriangleBuffer&) const in ProceduralBoxGenerator.o

更新

我发现了两个与此非常接近的问题,但问题依赖于差异。

在: question 1question 2

但在我的情况下,我需要转换2个模板类而不是相同的类,但2个实例。我希望这会有所帮助!

3 个答案:

答案 0 :(得分:1)

尝试确保编译器知道您的朋友声明应该是模板专业化,而不是声明全新的非模板函数:

friend Vector3<T> operator* <> (const Quaternion<T>& lhs, const Vector3<T>& rhs);

这个常见错误在C ++ FAQ here中进行了讨论。

答案 1 :(得分:0)

SSCCE开始:

template<typename T> class Quaternion { };
template<typename T> class Vector3 { };
template<typename T> class Point3 {
public:
  operator Vector3<T>() const { return Vector3<T>(); }
  friend Vector3<T> operator*( const Quaternion<T>& lhs, const Vector3<T>& rhs);
};
template<typename T>
Vector3<T> operator*(const Quaternion<T>& lhs, const Vector3<T>& rhs) { }
int main() {
  Quaternion<float> orientation_;
  Point3<float> pt;
  Vector3<float> vec = orientation_*pt;
  return 0;
}

使用gcc 4.7编译,我得到以下内容:

x.cc:6:79: warning: friend declaration ‘Vector3<T> operator*(const Quaternion<T>&,
                    const Vector3<T>&)’ declares a non-template function
                    [-Wnon-template-friend]
x.cc:6:79: note: (if this is not what you intended, make sure the function template
                 has already been declared and add <> after the function name here) 
Undefined symbols for architecture x86_64:
  "operator*(Quaternion<float> const&, Vector3<float> const&)", referenced from:
      _main in ccKgI7Ru.o

这指出了问题:您要声明朋友功能,而不是朋友模板。要解决这个问题,你必须做两件事:确保模板声明在这条友谊线之前,并添加尖括号:

template<typename T>
Vector3<T> operator*(const Quaternion<T>& lhs, const Vector3<T>& rhs) { }

template<typename T> class Point3 {
public:
  operator Vector3<T>() const { return Vector3<T>(); }
  friend Vector3<T> operator*<>(const Quaternion<T>& lhs, const Vector3<T>& rhs);
};

这会将链接器错误转换为编译器错误:

error: no match for ‘operator*’ in ‘orientation_ * pt’
note: candidate is:
note: template<class T> Vector3<T> operator*(const Quaternion<T>&, const Vector3<T>&)
note:   template argument deduction/substitution failed:
note:   ‘Point3<float>’ is not derived from ‘const Vector3<T>’

这是有道理的:宣布一个朋友是一个关于谁可以访问哪些数据的声明;它对模板解析的自动类型转换没有帮助。所以我建议如下:

template<typename T> class Point3 {
public:
  operator Vector3<T>() const { return Vector3<T>(); }
  friend Vector3<T> operator*(const Quaternion<T>& lhs, const Point3& rhs) {
    return lhs * Vector3<T>(rhs);
  }
};

这声明了一个新的内联好友操作符,它将明确地执行你想要的转换。

答案 2 :(得分:0)

实际上为了使它工作,我必须给Point类中的朋友声明的函数赋一个体。我想做像Scott Meyers的Effective C ++(第3版)第46项。就像这个问题一样,link

关键是里面的功能必须有一个体。所以Point3现在变成了(我已经剥掉了像MvG那样无用的代码,谢谢。

template<typename T> class Point3 {
public:
  operator Vector3<T>() const { return Vector3<T>(); }
  friend Vector3<T> operator*( const Quaternion<T>& lhs, const Vector3<T>& rhs)
  {
    return lhs*rhs;
  }  
};

这使得一切都编译和链接。 lhs rhs将调用Quaternion.h中定义的运算符(包含在Point3的头文件中)。

我不明白的是为什么我没有重复的符号。 对我来说,Point3类中的friend运算符*和Quaternion.h头文件中定义的operator *是相同的签名,但它编译和链接。