编译QuickHull代码遇Xcode警告及链接错误问题求助
我在编译代码时遇到了链接错误,同时Xcode弹出警告:此处需要实例化变量'quickhull::QuickHull::Epsilon',但无可用定义。
相关代码片段
namespace quickhull { template<> const float QuickHull<float>::Epsilon = 0.0001f; template<> const double QuickHull<double>::Epsilon = 0.0000001; /* * Implementation of the algorithm */ template<typename T> ConvexHull<T> QuickHull<T>::getConvexHull(const std::vector<Vector3<T>>& pointCloud, bool CCW, bool useOriginalIndices, T epsilon) { VertexDataSource<T> vertexDataSource(pointCloud); return getConvexHull(vertexDataSource,CCW,useOriginalIndices,epsilon); } // 疑似笔误的全局函数声明 ConvexHull<FloatType> getConvexHull(const std::vector<Vector3<FloatType>>& pointCloud, bool CCW, bool useOriginalIndices, FloatType eps = Epsilon); template<typename FloatType> class QuickHull { using vec3 = Vector3<FloatType>; static const FloatType Epsilon; ConvexHull<FloatType> getConvexHull(const VertexDataSource<FloatType>& pointCloud, bool CCW, bool useOriginalIndices, FloatType eps); public: // Computes convex hull for a given point cloud. // Params: // pointCloud: a vector of of 3D points // CCW: whether the output mesh triangles should have CCW orientation // useOriginalIndices: should the output mesh use same vertex indices as the original point cloud. If this is false, // then we generate a new vertex buffer which contains only the vertices that are part of the convex hull. // eps: minimum distance to a plane to consider a point being on positive of it (for a point cloud with scale 1) ConvexHull<FloatType> getConvexHull(const std::vector<Vector3<FloatType>>& pointCloud, bool CCW, bool useOriginalIndices, FloatType eps = Epsilon); };
调用代码
QuickHull<float> qh; // Could be double as well hull = qh.getConvexHull(pointCloud, true, false);
链接错误信息
"quickhull::QuickHull
::Epsilon", referenced from:
"quickhull::QuickHull::getConvexHull(std::__1::vector<quickhull::Vector3 , std::__1::allocator<quickhull::Vector3 > > const&, bool, bool, float)"
嘿,我来帮你搞定这个问题!你遇到的是C++模板静态变量的经典链接问题,结合你的代码来看,主要有两个小问题在作祟:
1. 先干掉那个多余的全局函数声明
你代码里有这么一行:
ConvexHull<FloatType> getConvexHull(const std::vector<Vector3<FloatType>>& pointCloud, bool CCW, bool useOriginalIndices, FloatType eps = Epsilon);
这明显是个笔误!它不属于全局命名空间,应该是QuickHull类的成员函数,但你已经在类内部和模板实现里写了正确的版本。这个全局声明会让编译器混乱,误以为要调用一个不存在的全局函数,进而找不到Epsilon的定义,直接删掉它就好。
2. 调整模板静态变量特化的位置
你现在把Epsilon的显式特化写在了QuickHull类定义之前,这会导致编译器在处理类的成员函数(比如带默认参数的getConvexHull)时,还不知道Epsilon的特化存在,自然会报“无可用定义”的警告。
把特化代码移到模板类定义之后,像这样:
namespace quickhull { // 先定义模板类 template<typename FloatType> class QuickHull { using vec3 = Vector3<FloatType>; static const FloatType Epsilon; ConvexHull<FloatType> getConvexHull(const VertexDataSource<FloatType>& pointCloud, bool CCW, bool useOriginalIndices, FloatType eps); public: ConvexHull<FloatType> getConvexHull(const std::vector<Vector3<FloatType>>& pointCloud, bool CCW, bool useOriginalIndices, FloatType eps = Epsilon); }; // 再放显式特化 template<> const float QuickHull<float>::Epsilon = 0.0001f; template<> const double QuickHull<double>::Epsilon = 0.0000001; // 然后是模板成员函数的实现 template<typename T> ConvexHull<T> QuickHull<T>::getConvexHull(const std::vector<Vector3<T>>& pointCloud, bool CCW, bool useOriginalIndices, T epsilon) { VertexDataSource<T> vertexDataSource(pointCloud); return getConvexHull(vertexDataSource,CCW,useOriginalIndices,epsilon); } // 别忘了实现处理VertexDataSource的那个重载 template<typename T> ConvexHull<T> QuickHull<T>::getConvexHull(const VertexDataSource<T>& pointCloud, bool CCW, bool useOriginalIndices, T eps) { // 这里放你的算法核心代码 } } // namespace quickhull
3. 头文件场景的额外处理(如果代码在头文件里)
如果这些代码是写在头文件中的,为了避免多个编译单元包含时出现重复定义的链接错误,给特化加上inline关键字(C++17及以后支持):
template<> inline const float QuickHull<float>::Epsilon = 0.0001f; template<> inline const double QuickHull<double>::Epsilon = 0.0000001;
这样编译器就会确保这个特化在整个程序中只定义一次。
为什么这样能解决?
- 移除错误的全局函数后,编译器会正确关联到
QuickHull类的模板成员函数,使用类内部声明的Epsilon。 - 把特化移到类定义之后,编译器在处理带默认参数的
getConvexHull时,已经知道Epsilon的具体定义,警告就会消失。 inline关键字解决了头文件被多次包含时的重复定义问题,链接错误自然也就没了。
内容的提问来源于stack exchange,提问作者andre

