Understanding Template Compilation
To use a template, its full definition, not merely its declaration, must be in scope.
Using a Container Whose Code You Cannot See
Suppose your program needs a container that holds a list of numbers. In C++ the standard library offers vector, which is a template: a single description that the compiler can turn into a concrete container for whatever element type you request. A vector of integers and a vector of strings both come from the same template. Using a template is not as simple as calling a function whose signature you already know. The compiler has to be able to see the full template in order to produce the specialized code you need. This article is about that requirement, often called the scope requirement for templates, and about the trouble it can cause when something goes wrong.
What do you think happens?
To write vector<int> in your program, is it enough for the compiler to know that a template named vector exists, or must it see the full template definition?
Reveal answer
The compiler must be able to see the full template definition.
To use a template, its full definition, not merely its declaration, must be in scope. The compiler needs the body of the template to generate the specialized code for the element type you request.
Declarations Versus Definitions
A declaration tells the compiler that something exists and describes its shape. A definition supplies the complete body that goes with that shape.
For ordinary functions and classes you may often see a declaration in one place and a definition in another, and a program can still make progress using the declaration alone in many situations. For templates, the distinction carries real weight. A declaration by itself is not enough to use the template, because the compiler needs the complete body to produce the specialized code for each element type you request.
Tracing a vector<int> Use Through the Compiler
Follow one use from start to finish. When your source code contains a use such as vector<int>, the compiler knows that it needs a container specialized for int. To produce that specialized code, it must be able to see the template definition. If the full definition is in scope at that point, the compiler can proceed. If only a declaration is visible, the template cannot be used as written.
Deciding Whether vector<int> Can Be Used
A source file contains a use of vector<int>. Only a declaration of the vector template is visible at that point. Can the compiler produce the specialized container?
- 1Identify the requirement
To use a template, its full definition must be in scope. A declaration alone does not satisfy this.
- 2Check what is visible
The compiler sees only the declaration, which says the template exists and describes its shape.
- 3Apply the rule
Without the complete body, the compiler cannot generate the specialized code for int elements.
- 4Fix the scope
Making the full definition available, through a standard header such as vector, lets the same use succeed.
The use cannot compile with only a declaration in scope. Once the full definition is in scope, the compiler can produce the specialized container for int.
The Role of the vector Header
The standard header vector holds the definition of the vector template. When your program includes that header, the full definition of the template becomes available to the compiler, and you can use vector with your own element types. The header is therefore not just a name tag. It carries the complete template that the compiler needs.
- The include line brings the full vector template definition into the file.
- vector<int> asks the compiler to produce a container specialized for int.
- Because the definition is in scope, the use of vector<int> can be compiled.
If the include line were removed, the same use would rely on a template definition that is no longer in scope. This is the practical meaning of the scope requirement: the header is the link between your code and the body the compiler must read.
Why Template Errors Surface Late
A type error involving a template can be detected uncomfortably late in the compilation process. The compiler discovers the problem only after it combines information from several places: the template definition, the place where it is used, and the types supplied at that use. The mistake depends on how these pieces fit together, so the compiler cannot always report it at the moment a programmer might first expect.
Mistakes Beginners Make With Template Scope
Common mistake Assuming a declaration alone is enough to use a template
Why it failsThe compiler must see the full template definition to generate the specialized code for the element type requested.
Do this insteadMake sure the header that provides the definition, such as vector, is included before the use.
Common mistake Treating the standard header as just a name tag
Why it failsA header such as vector carries the complete template the compiler needs, not only the name.
Do this insteadThink of an include line as bringing the template body into scope for the compiler.
Common mistake Reading only the first line of a long template error
Why it failsThe error can point into library code while the root cause is the use in your own file.
Do this insteadTrace back from the reported location to the line where you supplied the element type.
Tracing Scope in a Small Program
A program uses std::vector<double> in a function, but the file does not include the vector header anywhere above that use. Trace what the compiler can see at the point of use. Does it have the full template definition in scope? What change would make the use valid?
Hints
- Ask which step in the flowchart needs the template body rather than only its declaration.
- The standard header vector is where the definition of the vector template is provided.
- Adding the include line before the use puts the full definition in scope.
Key Takeaways
- To use a template, its full definition, not merely its declaration, must be in scope.
- A declaration says a template exists and describes its shape, while a definition supplies the complete body the compiler needs to generate specialized code.
- The standard header vector holds the definition of the vector template, so including it makes the template usable with your own element types.
- Because the compiler combines information from the template definition, the use, and the supplied types, template type errors can be detected late and produce hard-to-read messages, so trace them back to the use that caused them.