在使用GCC编译以下代码时,我遇到了一个未定义的引用错误4.7.2 20130108在x86_64-suse-linux下通过命令:
g++ main.cpp func.cpp -I/path/to/eigenlibrary/eigen_3.2.1
错误信息如下:
main.cpp:(.text+0x1d): undefined reference to `void f<2>(std::vector<Eigen::Matrix<double, 2, 2,
((Eigen::._84)0)|((((2)==(1))&&((2)!=(1)))?
((Eigen::._84)1) : ((((2)==(1))&&((2)!=(1)))?((Eigen::._84)0) : ((Eigen::._84)0))), 2, 2>,
std::allocator<Eigen::Matrix<double, 2, 2, ((Eigen::._84)0)|((((2)==(1))&&((2)!=(1)))?
((Eigen::._84)1) : ((((2)==(1))&&((2)!=(1)))?((Eigen::._84)0) : ((Eigen::._84)0))), 2, 2> > >&)'
请注意,这与模板实现与头文件分离的事实无关,因为没有模板函数的(通用)实现,而只有模板专门化。模板专门化的实现不能放在头文件中,因为这会产生多个定义错误。
另一个奇怪的事情是,如果我改变main.cpp (eigen/Dense和vector)中前两个头包含的顺序,错误不会发生。我对此没有理解,任何超越"简单地改变标题包含的顺序"的帮助将是非常感激的。
main.cpp:
#include <vector>
#include <Eigen/Dense>
//error does not occur once I change order of header inclusion like so:
//#include <Eigen/Dense>
//#include <vector>
#include "func.h"
int main() {
std::vector<Eigen::Matrix<double, 2, 2> > m;
f<2>(m);
}
func.h
#ifndef FUNC_H
#define FUNC_H
#include <Eigen/Dense>
#include <vector>
template <int N>
void f(std::vector<Eigen::Matrix<double, N, N> >& m);
template <> void f<2>(std::vector<Eigen::Matrix<double, 2, 2> >& m);
#endif
func.cpp
#include "func.h"
#include <vector>
template <>
void f<2>(std::vector<Eigen::Matrix<double, 2, 2> >& m) {}
在func.h
中,你的模板特化声明应该是:
template <> void f<2>(std::vector<Eigen::Matrix<double, 2, 2> >& m);
因此,将N=2
全部填满,就像您对func.cpp
的定义所做的那样。
注意,如果您将inline
添加到定义中,您应该能够在func.h
中定义模板专门化。
我可以使用GCC 4.6.4, 4.7.4, 4.8.2, 4.9.0,但不使用Clang 3.4.2,所有在Arch Linux上重现失败:
$ echo $'func.hn---'; cat func.h; echo $'---nfunc.c++n---'; cat func.c++; echo $'---nmain.c++n---'; cat main.c++; g++-4.6 -I/usr/include/eigen3 main.c++ func.c++; ./a.out
func.h
---
#ifndef FUNC_H
#define FUNC_H
#include <Eigen/Dense>
#include <vector>
template <int N>
void f(std::vector<Eigen::Matrix<double, N, N> >& m);
template <> void f<2>(std::vector<Eigen::Matrix<double, 2, 2> >& m);
#endif
---
func.c++
---
#include "func.h"
#include <vector>
template <>
void f<2>(std::vector<Eigen::Matrix<double, 2, 2> >& m) {}
---
main.c++
---
#include <vector>
#include <Eigen/Dense>
//error does not occur once I change order of header inclusion like so:
//#include <Eigen/Dense>
//#include <vector>
#include "func.h"
int main() {
std::vector<Eigen::Matrix<double, 2, 2> > m;
f<2>(m);
}
我强烈建议联系Eigen开发人员。
我知道这是一个老问题,但我们在不同的环境中遇到过同样的问题,我想我应该分享一下我们的解决方案。
对于无法解决的链接错误的解决方案,至少对于GCC 4.8.2-19ubuntu1,是替换function .h
中的以下内容template <int N>
void f(std::vector<Eigen::Matrix<double, N, N> >& m);
template <int N>
void f(std::vector<Eigen::Matrix<double, N, N, 0> >& m);
注意,第四个模板参数显式地以0给出,这是链接器错误
中模板函数声明中显示的表达式的结果。((Eigen::._84)0)|((((2)==(1))&&((2)!=(1)))?
((Eigen::._84)1) : ((((2)==(1))&&((2)!=(1)))?((Eigen::._84)0)
表达式来自第四个模板参数的默认值,该值在Eigen ForwardDeclarations.h
文件中作为
AutoAlign |
( (_Rows==1 && _Cols!=1) ? RowMajor
: (_Cols==1 && _Rows!=1) ? ColMajor
: EIGEN_DEFAULT_MATRIX_STORAGE_ORDER_OPTION )
在特征代码中还有另一种解决问题的方法,我将在一分钟内提到。
问题的原因似乎是GCC编译器延迟了该表达式的求值,并且该表达式涉及匿名枚举类型,其类型名称在表达式中显式给出(在本例中为(Eigen::._84)
)。此外,GCC编译器似乎使用计数器生成匿名enum
类型名,因此类型名取决于之前出现了多少匿名enum
类型,这可能因不同的编译单元而异。这就是为什么在包含Eigen/Dense
之前添加#include <vector>
或匿名enum
定义会触发这个问题。编译后的f<2>
中的类型名称很可能是(Eigen::._83)
,因此不匹配。
恐怕我不是编译器内部或c++标准深度方面的专家,也不能说这是GCC编译器的错误还是仅仅是解释不同的问题。
另一个涉及修改特征码并且似乎有效的解决方案是为Constants.h
中的enum
提供一个名称,该名称定义了AutoAlign
, RowMajor
和ColMajor
,因为问题是使用匿名enum
类型。例如:
/** ingroup enums
* Enum containing possible values for the p _Options template parameter of
* Matrix, Array and BandMatrix. */
enum MatrixOptionsType {
ColMajor = 0,
RowMajor = 0x1,
AutoAlign = 0,
DontAlign = 0x2
};
我不确定这是否会被Eigen开发人员所接受。