Common Compiler Errors¶
A lookup table: the message, what it means, and the fix. Everything quoted here was produced by
compiling the code shown, with the course's own flags (-std=c++17 -Wall -Wextra -Werror) on
GCC 14.2.0 — the messages are what you will actually see, not a recollection of them.
Method belongs elsewhere. For how to read a message fast — the file/line/column prefix, the notes, the habit of fixing the first error — see Reading Compiler Errors. This page is the catalogue.
Pairs with Module 1
Read the prefix first¶
file:line:column: severity: message. The first error is the one to fix; later errors are
often consequences of it, and can vanish when it does. Notes (note:) are not errors — they
are the compiler explaining its reasoning, and they usually contain the fix:
c04-private-access.cpp:11:9: note: declared private here
c04-private-access.cpp:17:7: note: field 'int Account::balance_' can be accessed via 'int Account::balance() const'
The compiler just told you which accessor exists. Reading the notes is most of the skill.
Syntax and punctuation¶
| Message | Cause | Fix |
|---|---|---|
error: expected ';' after class definition |
A class or struct closing brace without ; |
class Book { ... }; — note the semicolon |
error: expected '}' at end of input |
An unclosed brace above | Count braces; the error is usually before the reported line |
error: missing terminating " character |
An unterminated string literal | Close the quote |
error: expected primary-expression before 'int' |
Usually a consequence of a type that was not declared above (see the include row below) | Fix the earlier error |
Names and scope¶
did you mean is a suggestion worth reading — it is usually right. If the suggestion is not
what you wanted, check whether you declared the variable in the scope you think you did (a variable
declared inside an if block is not visible after it).
Includes¶
error: 'vector' is not a member of 'std'
note: 'std::vector' is defined in header '<vector>'; this is probably fixable by adding '#include <vector>'
2 | #include <iostream>
+++ |+#include <vector>
GCC 14 inlines the fix it wants. Do it; do not write std::vector with a forward declaration.
The trap: a missing include that compiles anyway¶
On this toolchain <iostream> pulls in <string> indirectly, so this compiles today and breaks
when a library update changes its own includes. The rule is therefore not "it compiled, so it is
fine" but: every header a file uses, it includes. If you use std::string, include <string>;
if you use std::sort, include <algorithm> — which, when missing, does fail:
Access and encapsulation¶
error: 'int Account::balance_' is private within this context
note: declared private here
note: field 'int Account::balance_' can be accessed via 'int Account::balance() const'
This error is the course working as intended. If you cannot reach the member, the class is protecting an invariant — add the operation you actually need, rather than making the member public.
Classes, inheritance and virtual functions¶
| Message | Cause | Fix |
|---|---|---|
error: cannot declare variable 's' to be of abstract type 'Shape' |
Constructing a class with a pure virtual function | Derive and implement it, or use a pointer/make_unique |
error: 'int Derived::value()' marked 'override', but does not override |
Signature differs — usually a missing const or a different parameter type |
Match the base declaration exactly |
error: 'virtual int Base::value() const' was hidden [-Werror=overloaded-virtual=] |
The derived function hides the base one instead of overriding | Fix the signature, or add using Base::value; |
error: no declaration matches 'void Widget::set(long int)' |
Out-of-class definition signature differs from the declaration | Match it; the note: candidate is: line shows the declaration |
error: 'class Member' has no member named 'x' |
Calling something that does not exist on that type | Check the class, and whether a const version is what you wanted |
The abstract-class message names exactly what is missing:
error: cannot declare variable 's' to be of abstract type 'Shape'
note: because the following virtual functions are pure within 'Shape':
note: 'virtual double Shape::area() const'
The definition-mismatch message shows both sides, which is the whole diagnosis:
error: no declaration matches 'void Widget::set(long int)'
note: candidate is: 'void Widget::set(int)'
Types and conversions¶
Braces are the compiler asking "did you mean to lose that?" Either use the right type, or narrow explicitly with a cast after checking. Parentheses would have compiled silently, which is why the course prefers braces.
Templates: errors that point somewhere unhelpful¶
template <typename T>
T longest(const std::vector<T>& items)
{
for (const T& item : items) { if (item.size() > best.size()) { /* ... */ } }
}
c13-template-error.cpp: In instantiation of 'T longest(const std::vector<T>&) [with T = int]':
c13-template-error.cpp:26:25: required from here
error: request for member 'size' in 'item', which is of non-class type 'const int'
Read it backwards. The required from here line is where the use is; the error is in the
template body. The template is fine for std::string and meaningless for int — so the diagnosis
is not "fix the line" but "this template assumed a member the type does not have". Once you see
[with T = int], the error has a type in it, and that is the answer.
Warnings, which -Werror turns into errors¶
| Message | Cause | Fix |
|---|---|---|
error: unused variable 'unused_local' [-Werror=unused-variable] |
Declared and never used | Remove it, or (void)unused_local; if it is intentionally unused |
error: unused parameter 'factor' [-Werror=unused-parameter] |
A parameter you never read | Remove it, or name the intent: int factor /*unused*/ |
error: control reaches end of non-void function [-Werror=return-type] |
A path returns nothing | Return on every path — an undefined return value is not "whatever you expect" |
error: reference to local variable 'local' returned [-Werror=return-local-addr] |
Returning a reference to a local | Return by value, or return something that outlives the call |
error: catching polymorphic type '...' by value [-Werror=catch-value=] |
catch (std::exception e) |
catch (const std::exception& e) |
error: 'throw' will always call 'terminate' [-Werror=terminate] |
throw in a destructor or a noexcept function |
Report the failure from an explicit close(), not from the destructor |
That line is -Werror doing its job. Fix the warning; do not remove the flag — the course uses it
precisely so that a warning cannot become a bug later.
Linker errors: no file, no line¶
Compilation succeeded and the link failed. No single file is wrong, so the message names no place. On this toolchain the message is only:
It does not name the symbol. The exit status differs between cases and does not classify them
reliably, so do not read it. Use nm -C on the object files to find the symbol yourself:
g++ -std=c++17 -Wall -Wextra -Werror -c main.cpp -o main.o
nm -C main.o | grep ' U ' # U = undefined: needed here, defined elsewhere
nm -C widget.o | grep ' T ' # T = defined here
| Case | What linking says | What nm -C shows |
|---|---|---|
| Undefined symbol — declared and called, never defined | collect2.exe: error: ld returned 116 exit status (5 on another attempt) |
U twice(int) in the object that calls it |
| Duplicate symbol — one function defined in two translation units | collect2.exe: error: ld returned 5 exit status |
T helper() in both objects |
No main |
collect2.exe: error: ld returned 1 exit status |
no main symbol at all |
Template defined in a .cpp — the definition is not visible to the user |
collect2.exe: error: ld returned 116 exit status |
U int twice<int>(int) where it is used, and zero twice symbols in the defining object |
Adding the missing definition is the fix, and it links and runs:
On a GNU/Linux toolchain you will instead see the symbol named directly, in a line of the form
undefined reference to '<symbol>'. That is a much kinder message. This course teaches the nm -C
route because it works on every toolchain, including the one used to write this page.
One environment trap. On some MinGW installations — including the machine this course was
written on — linking against the C++ standard library dynamically fails with the same
ld returned 116 exit status and no symbol at all, while -static succeeds:
g++ -std=c++17 -Wall -Wextra file.cpp -o file # fails with 116, writes a 0-byte file
g++ -std=c++17 -Wall -Wextra -static file.cpp -o file # works
If a perfectly good program fails to link and nm -C shows no missing or duplicated symbol, that
is the cause, not your code. Add -static to the link command and carry on — and if you are on a
college machine, tell whoever maintains it, because the real fix is to repair the toolchain.
A short order of operations¶
- Read the first error, and the notes under it.
- Fix only that, and rebuild. Errors cascade; the count is meaningless until the first is gone.
- If it is a warning-as-error, the flag name in the brackets names the rule — look it up if the message is not self-explanatory.
- If it is a link error, reach for
nm -Cand the table above. - If nothing makes sense, reduce until it does: delete code until the error changes, then look at what you last removed. See the Debugging Checklist.
Related: Reading Compiler Errors · Common OOP Bugs · Debugging Checklist · Setup