Undefined reference error from GCC using a template with a std::vector and an Eigen Matrix?

こ雲淡風輕ζ 提交于 2021-01-28 12:43:59

问题


I am experiencing an undefined reference error, when compiling the following code using GCC 4.7.2 20130108 under x86_64-suse-linux via the command:

g++ main.cpp func.cpp -I/path/to/eigenlibrary/eigen_3.2.1

The error message reads:

     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> > >&)'

Please note that this has nothing to do with the fact that the template implementation is separated from the header file, because there is no (generic) implementation of the template function, but only a template specialization. The implementation of the template specialization can not be put into the header file, because this yields multiple definition errors.

Another strange thing here is that if I change the order of the first two header inclusions in main.cpp (Eigen/Dense and vector) the error does not occur. I have no understanding for this and any help that goes beyond 'simply change the order of the header inclusions then' would be highly appreciated.

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

回答1:


In func.h, your template specialization declaration should read:

template <> void f<2>(std::vector<Eigen::Matrix<double, 2, 2> >& m);

So, with the N=2 filled in throughout, as you did with the definition in func.cpp.

Note that you should be able to define your template specialization in func.h if you add inline to the definition.


I can reproduce the failure using GCC 4.6.4, 4.7.4, 4.8.2, 4.9.0, but NOT Clang 3.4.2, all on Arch Linux:

$ echo $'func.h\n---'; 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);
}

I strongly suggest contacting the Eigen developers about this.




回答2:


I know that this is an old question, but we've run into the same problem in a different context, and I thought I would share our solution.

A solution to the unresolved link error, at least for GCC 4.8.2-19ubuntu1, is to replace the following in func.h

template <int N>
void f(std::vector<Eigen::Matrix<double, N, N> >& m);

with

template <int N>
void f(std::vector<Eigen::Matrix<double, N, N, 0> >& m);

Note that the forth template argument is explicitly given as 0, which is the result of the expression shown in the template function declaration in the linker error,

((Eigen::._84)0)|((((2)==(1))&&((2)!=(1)))?
    ((Eigen::._84)1) : ((((2)==(1))&&((2)!=(1)))?((Eigen::._84)0)

The expression comes from the default value for the forth template parameter, which is given in the Eigen ForwardDeclarations.h file as

AutoAlign |
( (_Rows==1 && _Cols!=1) ? RowMajor
: (_Cols==1 && _Rows!=1) ? ColMajor
: EIGEN_DEFAULT_MATRIX_STORAGE_ORDER_OPTION )

There is another way to fix the problem in the Eigen code, which I'll mention in a minute.

The cause of the problem seems to be that the GCC compiler delays the evaluation of this expression and that the expression involves anonymous enum types whose type names are explicitly given in the expression ((Eigen::._84) in this case). Further, the GCC compiler appears to generate anonymous enum type names using a counter, and therefore the type name depends on how many anonymous enum types have appeared before, which can vary for different compilation units. This is why adding #include <vector> or an anonymous enum definition before including Eigen/Dense triggers the problem. The type name in the compiled f<2> is likely (Eigen::._83), and thus the mismatch.

I'm afraid that I'm not an expert on the internals of compilers or the depths of the C++ standard, and can't say if this a bug in the GCC compiler or simply a matter of a difference of interpretation.

Another solution that involves modifying the Eigen code and seems to work is to provide a name for the enum in Constants.h that defines AutoAlign, RowMajor, and ColMajor, as the issue is in using anonymous enum types. For example:

/** \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
};

I'm not sure if that would be acceptable to the Eigen developers, though.



来源:https://stackoverflow.com/questions/24730981/undefined-reference-error-from-gcc-using-a-template-with-a-stdvector-and-an-ei

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!