简体   繁体   中英

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

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

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM