【问题标题】:I don't understand this link error我不明白这个链接错误
【发布时间】:2013-06-13 02:29:07
【问题描述】:

我不明白这个链接错误。 我有 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;

我也有一个四元数类,我相信细节无关紧要,但这个类有一个非成员运算符*:

 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

更新

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

在: question 1question 2

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

【问题讨论】:

  • 你确实没有有两个类和一个四元数类。您有三个模板
  • 模板化operator* 至少需要一个实例化。它应该放在使用它的源文件包含的头文件中,或者在包含operator* 的源文件中显式实例化
  • @PeteBecker 是的,我有 3 个模板类。但我相信问题出在 2 个模板类 Vector3 和 Point3 上。
  • 编译器需要模板函数定义在实例化点可见。不清楚operator*的定义是在头文件中还是在源文件中。在您尝试使用它之前,它需要在您包含的头文件中。
  • 你能为此创建一个SSCCE 吗?

标签: c++ c++11


【解决方案1】:

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

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

C++ 常见问题解答here 中讨论了这个常见错误。

【讨论】:

    【解决方案2】:

    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);
      }
    };
    

    这声明了一个新的内联友元运算符,它将明确地执行您想要的转换。

    【讨论】:

    • 感谢您向我展示这一点,对于 SSCCE,在我看到您的之前,它在我看来真的是赤裸裸的。我实际上找到了如何使它工作。我不喜欢最终的解决方案,但它是我试图实现的解决方案,就像在有效 C++ 的第 46 条中一样。
    【解决方案3】:

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

    关键是里面的函数必须有一个body。所以 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;
      }  
    };
    

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-12
      • 1970-01-01
      • 2011-11-19
      • 2011-12-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多