When programming, I often encounter cryptic error messages that make debugging a drawn-out and tedious affair. It appears that most compiler authors don't make explaining the error messages of their compilers a high priority, even though most beginners will stumble upon these errors eventually and will waste precious time trying to divine what the messages are actually trying to say - a bit like trying to learn a foreign language and understanding only half the vocabulary. Usually I search for error messages with my favorite search engine and hope to find an explanation or at least associated examples, but I'm unsuccessful too frequently for my taste. In the hopes of saving other programmers time, I have collected a couple of C++ error messages that I encountered while using the GNU C++ compiler (versions 4.9.2 to 9.4.0) together with minimal code examples to reproduce the errors, corrections for those examples, and occasional explanations. All code was compiled with
g++ --std=c++11 -Wall source.cpp
.
table of contents
incomplete type
instead of
results in
... has incomplete type and cannot be defined
.
incomplete template
instead of
results in
expected class-name before ‘{’ token
.
or in
or in
missing template arguments before
The problem in all cases is using a template class without template arguments. If you actually want to use a template class without explicitly using template arguments, you'd have to provide default template arguments in the definition of the template in most cases.
incomplete template the second
instead of
results in
expected type-specifier before ...
give your template parameters real values
instead of
results in several error messages, among them
request for member ... in ..., which is of non-class type
But since this is probably not the first error message, you should pay attention to the first error message and fix that.
missing default constructor
or equivalently
instead of
results in
no matching function for call to ‘A::A()’
.
another example for the same problem:
instead of
relevant quote from
cppreference.com that explains why this might be confusing (emphasis mine):
If no user-declared constructors of any kind are provided for a class type (struct, class, or union), the compiler will always declare a default constructor as an inline public member of its class.
So, if you add any non-default constructor, you have to provide the default constructor as well (if you want to use it), it will not be added automatically.
initializing reference using a constant
instead of
results in
invalid initialization of non-const reference of type ... from an rvalue of type
or in
cannot bind non-const lvalue reference of type ... to an rvalue of type
The problem is that a reference must point to an lvalue, in essence a variable, i.e. some place in memory where a value can be stored. An rvalue, in essence a constant or the value of some expression like "1+2", is not stored anywhere by default, it possesses no storage location by itself, and consequently the reference has nothing to point to. If you store the rvalue in an lvalue first, then you can reference that lvalue just fine.
A somewhat unrelated problem is using
instead of
which also resulted in
invalid initialization of non-const reference of type ... from an rvalue of type
in older compiler versions.
Newer compiler produce the better error message
invalid conversion from ‘int*’ to ‘int’
The problem is just mixing up references and pointers. "&variable" in an expression always produces a pointer and you can't assign a pointer value to a reference.
deleting instance of virtual class
Using
instead of
results in
deleting object of polymorphic class type ‘A’ which has non-virtual destructor might cause undefined behavior
The warning explains the problem well, but careless copy/paste can make classes virtual by accident, which can make this warning confusing. Either make all methods non-virtual or add a virtual constructor to solve this.
Doubled method
instead of
results in
... cannot be overloaded with
Another example of what can happen accidentally due to careless copy/paste, especially in large classes.
Constructor has wrong name
instead of
results in
ISO C++ forbids declaration of ... with no type
Another example of careless copy/paste. When you copy a class and then rename it, be sure to rename all constructors and other references to the old class name as well.
Don't forget your subclass declaration
instead of
results in
‘class ...’ has no member named
This one shouldn't be all that confusing, but it might still be surprising to see if you are convinced that you have already declared B a subclass of A.
Trying to convert a lambda to a function pointer
instead of
results in
no matching function for call to ‘A::f(main()::<lambda()>)’
in older compiler versions or results in
cannot convert ‘main()::<lambda()>’ to ‘void (*)()’
in newer compiler versions. The new error message is somewhat understandable, but the old error message is very confusing if you don't understand the difference between lambdas with captures, lambdas without captures and function pointers. The code works if the lambda captures nothing (because such a lambda is just a normal function and the compiler can produce a pointer to its code), but fails to compile with a confusing error message if the lambda captures something. As far as I know, there is no simple way to convert such a lambda to a normal function, because the lambda needs to keep track of the captured variables and a function pointer can not carry this additional information (the function pointer is just a single number - the function code address). Lambdas without captures are of course much less powerful. So if you really need lambdas with captures, try the following:
Of course this requires a template method and this code could never be compatible with C. Lambdas without captures are probably compatible with C functions expecting function pointers and so cross-linking should be possible.
Redefinition of the same function in a different source file
If you have the three files test.cpp
test2.cpp
and test.hpp
and compile both test.cpp and test2.cpp simultaneously, you won't get a compiler error (because the two files are compiled separately and so the compiler only ever sees one definition of function f at a time), but you will get the linker error
multiple definition of ... first defined here
, probably without any useful information about the location of the error. The problem is that the compiler outputs for test.cpp and test2.cpp have to be linked together for the final executable file, but both outputs contain definitions of function f - the linker doesn't know which one to use. But because by that time most information in the original source file has been thrown away, the linker can not give you much information about the cause/source-location of the error. The solution is to not put function definitions into your header files, only function declarations without function bodies should go there. The code
will compile if function f is declared as "static void f()" and this usage of "static" does not do the same as "static" in a class method declaration - yes, I also find that name collision annoying.
using a non-constant iterator on a constant string
instead of
results in
conversion from ‘__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >’ to non-scalar type ‘std::basic_string<char>::iterator {aka __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >}’ requested
in older compiler versions or
conversion from ‘__normal_iterator<const char*,[...]>’ to non-scalar type ‘__normal_iterator<char*,[...]>’ requested
in newer compiler versions.
using a non-constant iterator on a constant vector
instead of
results in
In instantiation of _OIter std::copy_if ... no match for call to ... conversion of argument 2 would be ill-formed ... binding reference of type ... to ... discards qualifiers
The problem is the same as for the constant string: iterating over constant objects does not work with functions that expect access to non-constant variables.
automatic casting plus template deduction equals pain
instead of
will not result in a compiler error or linker error or even a runtime error, but it will break your heart and drive you insane. If you use 0 as an initialization for std::accumulate, C++ will consider it an integer, deduce that you want integer as std::accumulate's template parameter for the initialization value and automatically cast back and forth the value between integer and double, which of course includes rounding. Declaring that the accumulation function expects doubles is irrelevant. So giving the initialization value as 0.0 or better yet static_cast<double>(0.0) is probably what you want.
use delete() only on what has been created by new()
instead of
will also not result in a compiler error or linker error, but it will produce the runtime error
, because new() doesn't just reserve memory and create a pointer to that memory, it also creates management information that is needed so that delete()/free() can do its job. Thus you can't hand arbitrary pointers to delete();
trying to automatically cast a lambda
instead of
will result in
invalid user-defined conversion from ‘main()::<lambda(double)>’ to ‘char (*)(char)’
trying to use a member function as a member variable
instead of
will result in
invalid use of non-static member function
invoking undefined behavior
instead of
#include <iostream>
int main () {
float sums[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
for( unsigned int k = 0 ; k <= 16 ; k++ ) {
std::cerr << k << " " << sums[k] << std::endl;
}
}
will result in the warning
iteration 16u invokes undefined behavior [-Waggressive-loop-optimizations]
in older compiler versions or
‘#‘target_mem_ref’ not supported by dump_expr#<expression error>’ is used uninitialized in this function [-Wuninitialized]
in newer compilers when compiled with the -O3 flag for aggressive optimization. The warning is not the puzzling part, the puzzling part is that the loop end is simply optimized away and the loop runs for many iterations until the program dies with a segmentation fault (SIGSEGV), because the loop body tried to access a protected memory location without permission. So, don't compile your code with -O3 before you've gotten rid of all warnings. And after that, make sure your code still passes all tests.
forgetting std::pair
instead of
will result in
... is not a class, struct, or union type ... typedef typename _Alloc::value_type value_type;
and
no matching function for call to ‘std::allocator_traits ...
and
no members matching ‘std::vector ... ::_Base {aka std::_Vector_base ... ::_M_allocate’
and many other errors, which look very confusing, because the compiler has no clue what you want from it when trying to instantiate a template class with the wrong number of template parameters. One problem is that std::vector takes two template parameters, but the second one is usually not specified explicitly and instead left at its default value. That is one example of the conflict between powerful flexibility (of templates and container classes) and simplicity/understandability.
trying to allocate an array of pointers the wrong way
instead of
will result in
array bound forbidden after parenthesized type-id
I really don't get why, but removing the parentheses solves the problem.
trying implied multiplication for fun and education
instead of
will result in
expression cannot be used as a function
similarly
instead of
will result in the same error.
not everything is part of the std namespace
instead of
will result in
expected unqualified-id before ‘dynamic_cast’
trying to declare destructor without parentheses
instead of
will result in
invalid use of destructor ... as a type
an extra comma
instead of
will result in
expected primary-expression before ‘)’ token
main is not a variable
instead of
will result in
cannot declare ‘::main’ to be a global variable
accessing a member within the same (?) class
will result in
is private within this context
, because class A is a template and A<int> is not the same class as A<double>. They are not even related. If you thought class A is the same irrespective of template parameters, you might get the impression that method f just calls method g within the same class, which should not run into any visibility issues.
sometimes you have to be explicit about what is a type
instead of
results in
need ‘typename’ before ‘std::vector<T>::iterator’ because ‘std::vector<T>’ is a dependent scope
In related situations, you might get
dependent-name ‘...’ is parsed as a non-type, but instantiation yields a type
Using a function as a constant
instead of
results in
ISO C++ forbids comparison between pointer and integer
Using a template function without template argument
instead of
results in
no matching function for call to...
pointers to class methods have a hellish syntax
instead of
results in
must use ‘.*’ or ‘->*’ to call pointer-to-member function in ..., e.g.
But on the other hand, using
instead of
results in
invalid use of non-static member function ...
So you have to be really careful about parentheses, even though neither the error message or the Stroustrup book make this very clear.
std::stable_sort is not like std::sort
instead of
results in
binding reference of type ‘int&’ to ‘const int’ discards qualifiers
which is somewhat confusing, because it does work with std::sort instead of std::stable_sort. Of course you can just avoid using references in the comparator function if possible and suffer the cost of copying the vector elements.
switch without case
instead of
results in
and
label ... defined but not used
The second is just a warning, but always appears. The first is an error, but only appears if you have two switch blocks. This might be particularly confusing if you've never used a label and don't know what it is. Similarly
instead of
results in
expected ‘;’ before ‘:’ token
and
The difference is that labels can't start with digits, but case-expressions can.
Written by the author; Date 14.01.2026; © 2026 spinningsphinx.com
Paralinguistic/connotation key:
- Mocking
- Sarcasm, e.g. "Homeopathy fans are a really well-educated bunch"
- Statement not to be taken literally, e.g. "There is a trillion reasons not to go there"
- Non-serious/joking statement, e.g. "I'm a meat popsicle"
- Personal opinion, e.g. "I think Alex Jones is an asshole"
- Personal taste, e.g. "I like Star Trek"
- If I remember correctly
- Hypothesis/hypothetical speech, e.g. "Assuming homo oeconomicus, advertisement doesn't work"
- Unsure, e.g. "The universe might be infinite"
- 2 or more synonyms (i.e. not alternatives), e.g. "aubergine or eggplant"
- 2 or more alternatives (i.e. not synonyms), e.g. "left or right"
- A proper name, e.g. "Rome"
One always hopes that these wouldn't be necessary, but in the interest of avoiding ambiguity and aiding non-native English speakers, here they are. And to be clear: These are not guesses or suggestions, but rather definite statements made by the author. For example, if you think a certain expression would not usually be taken as a joke, but the author marks it as a joke, the expression shall be understood as a joke, i.e. the paralinguistic/connotation key takes precedence over the literal text. Any disagreement about the correct/incorrect usage of the expression may be ascribed to a lack of education and/or lack of tact on the part of the author if it pleases you.