使用带有std::vector和特征矩阵的模板时出现的未定义引用错误



在使用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, RowMajorColMajor,因为问题是使用匿名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开发人员所接受。

最新更新