问题
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