Templates and headers question

2019-01-19 08:06发布

The compiler says it can't find the reference for the function when I do this:

// link.h
template <class T>
    T *Link(T *&, T *(*)())

// link.cpp
template <class T>
T c:Link(T *&ChildNodeReference, T *(*ObjectCreator)()){

}

If I implement inside the class on the header it goes smoothly.

Please, I will work on the header until someone enlightens me about this.

There are somethings in C++ that are weirdly annoying. I know, there is a reason for this and etc. Even so, can't the compilers help you out about it -_-"

5条回答
疯言疯语
2楼-- · 2019-01-19 08:32

Templates are essentially semi-type-safe macros, hence the limitation.

Normal (non-template) functions can be compiled to native code residing object/library files, and then referenced with only a prototype available in the header, solely because there's only a single version of such a function.

With templates, C++ compiler has to compile each instantiation of the function separately. Obviously, it cannot do it "in advance", because the set of types for which you can instantiate the function is effectively unbounded (you can always define a new type in your code before calling the function). In fact, two instantiations of the same function template can be completely different. Consider this extreme case:

struct t1 {
  template <int>
  struct a {};
};

struct t2 {
  enum { a = 123 }; 
};

enum { b = 456, c = 789 };

template <class T>
void foo() {
   T::a<b>c;
}

Now if we call foo<t1>(), the statement inside it is a local variable declaration, because t1::a is a class template:

T::a<b> c;

But if we call foo<t2>(), the statement inside is an expression, because t2::a is an integral constant:

(T::a < b) > c;

This is just to show that compiler cannot meaningfully "compile" a template; it really has to mostly preserve the tokens.

Now, all that said, ISO C++ does actually provide the ability to separate declaration and definition of templates, so that they can be treated as normal functions, with declarations in .h files, and definitions in .cpp files. This is called "export templates", because you have to precede both declaration and definiton with keyword export:

// link.h
export template <class T>
T *Link(T *&, T *(*)());

// link.cpp
export template <class T>
T *Link(T *&ChildNodeReference, T *(*ObjectCreator)()) {
}

This is, however, a controversial feature of the Standard because of very high burden on implementation, and most popular implementations refuse to implement it; notably, g++, MSVC, and C++Builder do not implement it. The only compiler I know of that supports it is Comeau C++.

查看更多
地球回转人心会变
3楼-- · 2019-01-19 08:48

Templated implementations (not only definitions) have to be available at compile time.

So, the full template code is normally put in the header file.

查看更多
戒情不戒烟
4楼-- · 2019-01-19 08:52

Template code must be in the header. Sorry, I completely missed that! (and I thought I'd been coding C++ for years :P)

查看更多
淡お忘
5楼-- · 2019-01-19 08:55

Programming non-template code or non-inlined functions in headers is a Bad Thing™. The reason you have cpp files is to prevent redefinition of the same function code more than once, amongst other things.

The difference with templates is that the compiler practically doesn't touch them until your code instantiates a specialisation of that template which is why they need to have their source inside the header.

When the compiler finds an instantiation of a template specialisation (say List<int>), it goes back to the included template code and compiles it with that specialisation, which is why you don't have issues with redefinition of function code.

What you don't seem to understand is that this doesn't apply to non-templated code. All non-template code is compiled as normal and thus CPP files are needed to only define the code once then link it all together.

If you define functions inside a header, your linker will not link the compiled translation units because they have defined the same function more than once.

查看更多
Anthone
6楼-- · 2019-01-19 08:56

You forgot the * for the return type. So implementation is not matching definition. Add it and it should work:

T *c:Link(T *&ChildNodeReference, T *(*ObjectCreator)())
{
}

Implementation must be too in the header file under the class definition in order to be available at compile time.

查看更多
登录 后发表回答