0

I'm facing one linking related issue when writing some C++ code. When I declare a functor in header file ("class.h") and define in its source file ("class.cpp"), then try to use it in the main() function, the compiler (Clang 12) shows "undefined reference". But when I add another usage to the source file ("class.cpp"), the issue is gone.

So my question is: how this issue got solved by adding another usage of functor in source file?

Any advice appreciated!

Update of question

Thanks for the comments from Patrik and Eng. And the here is the link to the question that I've already read: Why can templates only be implemented in the header file?.

The code I provided are extracted from some code similar to the "Alternative solution" provided by Luc Touraille, which is the highest voted answer at this moment. And to be more specific, my question is about how:

Increase inc;
traverse(inc);

These two lines (in the source file "class.cpp") solved the issue. And I think implementing the function traverse() in header file can solve the "undefined reference" issue but cannot explain why the two lines work.

Sorry I'm quite new to compiler and linker, and I did made some searches about how they produce the compiled program. But unfortunately I haven't got a clue or proper understanding.


Source code below

class.h

#include <vector>

struct Increase {
  void operator()(int &);
};

class MyVector {
 public:
  MyVector() ;

  template<typename V>
  void traverse(V &);

 private:
  std::vector<int> _elems;
};

class.cpp

#include "class.h"

void Increase::operator()(int &a) {
  a++;
}

MyVector::MyVector() {
  _elems.push_back(1);
  _elems.push_back(2);
  _elems.push_back(3);
  _elems.push_back(4);

  // Just provide a usage for Increase and traverse.
  // Commenting the following two lines, the "undefined reference" error will appear.
  // Uncommenting them will solve the issue.
  // Increase inc;
  // traverse(inc);
}

template<typename V>
void MyVector::traverse(V &v) {
  for (int i = 0; i < _elems.size(); i++) {
    v(_elems[i]);
  }
}

main.cpp

#include "class.h"

int main() {
  MyVector v;

  Increase inc;
  v.traverse(inc);

  return 0;
}
David Yang
  • 21
  • 4
  • `traverse` has to be defined in the header because it's a template function. – Patrick Roberts Sep 03 '21 at 07:11
  • It's a function template. There are no template functions in C++. – Evg Sep 03 '21 at 07:13
  • 1
    @Evg call it what you will, my advice still stands. – Patrick Roberts Sep 03 '21 at 07:17
  • And the reason why uncommenting these lines solves the problem is that in that case a compiler will instantiate that template, which will be then found when `main.cpp` is compiled. – Evg Sep 03 '21 at 07:21
  • Thanks @Evg, I think you get my point. I'm trying to understand how the compiler instantiate the template with the two lines of code so that when `main.cpp` gets compiled, compiler can find the symbols. Could you please share some clues for me to continue on that? – David Yang Sep 03 '21 at 07:30
  • The general rule is simple: when a template is used in a compilation unit and its definition is visible, a compiler will instantiate that template. If definition is not visible, a compiler will just generate a function call - to do that, only a declaration is needed. When `main.cpp` is compiled, the only thing a compiler sees is a declaration, so it generates a function call, but it doesn't instantiate a template - it can't do it. When `class.cpp` is compiled, the definition is available, and that's why a compiler instantiates a template if it is used. – Evg Sep 03 '21 at 07:42
  • Also bear in mind that compilation is done in two steps: first `.cpp` files are translated into object files and then object files are linked together. "Undefined reference" is not a compilation error, but a linking one. A linker doesn't care when and why a template was instantiated. Actually, when you put a template into a header, it will be instantiated in all compilation units that use it. All instantiations will be identical and a linker will choose just one of them. Also see what explicit instantiations and `extern template`s are. – Evg Sep 03 '21 at 07:44
  • Hi @Evg, thanks for the advice. So basically, my provided two lines of code will cause the compiler to instantiate the template, which is implicit template instantiation, so that later linker can resolve the reference of the function call. And the alternative solution given by answer under the other question "https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file" is the explicit instantiation, is this correct? – David Yang Sep 03 '21 at 08:10
  • And with @Evg's advice, I think this should be the answer to this question: https://en.cppreference.com/w/cpp/language/function_template (plus Evg's comments of course). Thank you very much! – David Yang Sep 03 '21 at 08:11
  • Correct. In general you should not rely on accidental instantiations like that in the code example in this question, of course. The best way is to put templates into headers unless you have a strong reason to do something else. – Evg Sep 03 '21 at 08:36

0 Answers0