模板继承:找不到符号

Template inheritance: Symbol not found

我在使用模板和继承时遇到问题。我已经将一些有问题的代码削减到我可以 post 的地方。我的代码如下:

///////////////////////////// My header.h file
#include <vector>

template <class T>
class Slider {
protected:
  std::vector<T> out;
public:
  Slider() : out() {}
  virtual void save_result();
};

template <class T>
class SlidingVar : public Slider<T> {
public:
  SlidingVar() {}
  void save_result() {}
};

template <class T>
void slide(std::vector<T> x, Slider<T> s) {}

///////////////////////////// My sliding_sd.cpp file
#include <vector>
#include "headers.h"

void sliding_var_window(std::vector<double> x) {
  SlidingVar<double> s;
  slide(x, s);
}

虽然以上都是 "pure C++" 代码,但我是在 R 包的上下文中执行此操作的,我的 sliding_var_window 函数将导出为可从 R 调用.

上面的编译没问题,但是当 R 使用 dlopen 加载它时,我得到 Symbol not found 关于 save_result() 函数的错误。奇怪的是,在我削减它之前,其他类似的功能工作得很好,但这个不是。这是构建顺序:

==> R CMD INSTALL --preclean --no-multiarch --with-keep.source tsevents

* installing to library ‘/Users/kwilliams/R/library/3.4’
* installing *source* package ‘TSEvents’ ...
** libs
clang++ -std=gnu++11 -I/usr/local/Cellar/r/3.4.2/lib/R/include -DNDEBUG  -I"/Users/kwilliams/R/library/3.4/Rcpp/include" -I/usr/local/opt/gettext/include -I/usr/local/opt/readline/include -I/usr/local/include   -fPIC  -g -O2 -c sliding_sd.cpp -o sliding_sd.o
clang++ -std=gnu++11 -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup -single_module -multiply_defined suppress -L/usr/local/opt/gettext/lib -L/usr/local/opt/readline/lib -L/usr/local/lib -L/usr/local/Cellar/r/3.4.2/lib/R/lib -L/usr/local/opt/gettext/lib -L/usr/local/opt/readline/lib -L/usr/local/lib -o TSEvents.so sliding_sd.o -L/usr/local/Cellar/r/3.4.2/lib/R/lib -lR -lintl -Wl,-framework -Wl,CoreFoundation
installing to /Users/kwilliams/R/library/3.4/TSEvents/libs
** R
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded
Error: package or namespace load failed for ‘TSEvents’ in dyn.load(file, DLLpath = DLLpath, ...):
 unable to load shared object '/Users/kwilliams/R/library/3.4/TSEvents/libs/TSEvents.so':
  dlopen(/Users/kwilliams/R/library/3.4/TSEvents/libs/TSEvents.so, 6): Symbol not found: __ZN6SliderIdE11save_resultEv
  Referenced from: /Users/kwilliams/R/library/3.4/TSEvents/libs/TSEvents.so
  Expected in: flat namespace
 in /Users/kwilliams/R/library/3.4/TSEvents/libs/TSEvents.so
Error: loading failed

我怀疑这确实与 RRcpp 无关,但这提供了我的构建环境,所以如果有人可以建议一种在纯 C++ 工具链中演示此问题的方法,那将是最受欢迎的。

另一个我不明白的花絮 - 当我查看目标文件中定义的符号时,save_result() 函数似乎具有 unsigned short return 类型,当我声明为 void:

% nm src/TSEvents.so | c++filt | grep result
0000000000002df0 unsigned short SlidingVar<double>::save_result()
                 U Slider<double>::save_result()

这正常吗?

我可以在添加 int main() { } 并使用 g++ 编译后重现链接器错误,而无需其他选项:

sliding_sd.cpp:(.rdata$_ZTV6SliderIdE[__ZTV6SliderIdE]+0x8): undefined reference to `Slider<double>::save_result()'

这意味着在Slider<double>的vtable(虚方法table)中存在对虚方法Slider<double>::save_result()的引用,在链接期间无法解析.原因是您在 template <class T> class Slider 中声明了 save_result() 但没有定义它(至少在您发布的代码中)。尽管 Slider<double>::save_result() 从未在您的示例中使用过,但必须对其进行定义(至少在使用普通 C++ 时,我不了解 R),因为它是 virtual。否则无法在链接期间为 Slider<double> 创建 vtable。

当改为

template <class T>
class Slider {
  /* ... */
  virtual void save_result() { }
};

链接错误消失了(至少在我的复制品中)。