How to provoke and fix common C++ error messages


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
is not a type
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
free(): invalid pointer
, 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

 

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
duplicate label ...
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
statement has no effect
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.