C++ build errors

C++ “undefined reference to” linker error: causes and fixes

An undefined reference message usually comes from the linker. The compiler has accepted a call because it saw a declaration, but the final build does not contain a matching function definition. This differs from a compile error, which means a source file could not be translated because its code was invalid or a name was unavailable.

Separate compilation and linking

A declaration tells the compiler a function's name, return type, and parameter types. A definition supplies the function body. A header commonly holds declarations, while one .cpp file provides the definition. The caller can compile using the header, but the linker still needs the object code that contains the function body.

When a compiler driver builds multiple source files, it usually compiles each translation unit and then links the resulting object files. A translation unit is a source file after preprocessing its included headers. If the caller refers to a function that no linked object defines, linking fails. The compiler does not automatically search every .cpp file in the folder.

Start by reading the diagnostic name and the build command. Check whether a definition exists, whether its signature matches the declaration, and whether its source file appears in the command. A misspelled function name or parameter type can create a different symbol from the one the caller requests. A file omitted from the command leaves a valid definition out of the final link.

Read an undefined reference

This single-file program declares and defines doubleValue. The compiler sees both the function signature and body, and the linker has the definition in the same translation unit.

#include <iostream>

int doubleValue(int value) {
    return value * 2;
}

int main() {
    std::cout << doubleValue(21) << '\n';
    return 0;
}

Build it with g++ -std=c++17 double-value.cpp -o double-value.

Expected output:

42

Now consider a multi-file program that includes a declaration but has no implementation file in the build. This is the failing case. The source compiles, but the link step reports an undefined reference to formatLabel(int).

Failing case: main.cpp

#include <iostream>
#include "label.h"

int main() {
    std::cout << formatLabel(7) << '\n';
    return 0;
}

Failing case: label.h

#pragma once

int formatLabel(int value);

Run g++ -std=c++17 main.cpp -o label-demo in the directory containing these files. The command omits a definition, so the linker fails with an undefined reference to formatLabel(int). Linker wording varies by platform; the stage and missing symbol identify the problem.

undefined reference to `formatLabel(int)'

What "collect2: error: ld returned 1 exit status" means

This final line says that the link step failed. It does not identify the missing symbol. Read the undefined reference to line above it to find the function or object the linker could not resolve. The exact wording and surrounding lines vary by compiler and version.

undefined reference to `formatLabel(int)'
collect2: error: ld returned 1 exit status

MSVC reports a related failure as LNK2019 unresolved external symbol. It may then report LNK1120 with the number of unresolved externals.

Add the source file to the command

Keep the declaration in the header and define the function in a source file. Then pass both source files to the compiler driver. The header is included by the caller so the compiler checks the call's signature. The implementation file contributes the function body to the link.

Each function that is called across files needs one matching definition in the program. Do not put an ordinary non-inline function definition into a header included by many source files; that can create multiple-definition errors. Templates and explicitly inline functions follow different rules, but a single implementation source is a clear pattern for ordinary functions.

Working case: main.cpp

#include <iostream>
#include "label.h"

int main() {
    std::cout << formatLabel(7) << '\n';
    return 0;
}

Working case: label.h

#pragma once

int formatLabel(int value);

Working case: label.cpp

#include "label.h"

int formatLabel(int value) {
    return value + 100;
}

Build the corrected version with g++ -std=c++17 main.cpp label.cpp -o label-demo.

Expected output:

107

If the linker still reports an undefined reference, compare the declaration and definition exactly, including namespaces, parameter types, and capitalization. Also make sure your build system recompiles changed files and links the resulting object file. An unresolved external in some toolchains describes the same general stage: a required symbol was not found during linking.

Missing or misspelled main

The program entry point must be named main. A typo such as mian lets the function compile, but the linker cannot find the required entry point.

Failing example: misspelled entry point

#include <iostream>

int mian() {
    std::cout << "Ready\n";
    return 0;
}

GCC diagnostic core:

undefined reference to `main'

The exact wording varies by compiler and version. MSVC reports LNK1561, "entry point must be defined." Rename the function to main.

Fixed example

#include <iostream>

int main() {
    std::cout << "Ready\n";
    return 0;
}

Expected output:

Ready

Undefined reference to vtable

A class can declare a virtual function without defining it. Constructing the class then requires a vtable, but the linker cannot build that table without the function definition. GCC can report the stable core undefined reference to `vtable for Shape'. MSVC reports LNK2001 for the missing Shape::draw definition.

Failing example: declared virtual function has no definition

class Shape {
public:
    virtual void draw() const;
};

int main() {
    Shape shape;
    (void)shape;
    return 0;
}

GCC diagnostic core:

undefined reference to `vtable for Shape'

Fixed example

#include <iostream>

class Shape {
public:
    virtual void draw() const;
};

void Shape::draw() const {
    std::cout << "Drawing a shape\n";
}

int main() {
    Shape shape;
    shape.draw();
    return 0;
}

Expected output:

Drawing a shape

Define every declared virtual function in one source file. The class then has the definitions its vtable needs.

Exercise

Create a three-file program with a unitTotal function. The function accepts an integer price and quantity and returns their product. Read those two integers in main and print the total. Sample input:

5 4

Expected output:

20

Hint 1: Put the function declaration in a header. Hint 2: Compile the caller and the implementation source in one command.

Show one possible solution

main.cpp

#include <iostream>
#include "total.h"

int main() {
    int price = 0;
    int quantity = 0;
    if (!(std::cin >> price >> quantity)) {
        return 1;
    }
    std::cout << unitTotal(price, quantity) << '\n';
    return 0;
}

total.h

#pragma once

int unitTotal(int price, int quantity);

total.cpp

#include "total.h"

int unitTotal(int price, int quantity) {
    return price * quantity;
}

Build the exercise with g++ -std=c++17 main.cpp total.cpp -o invoice.

Keep learning with CppPath

Continue with the Compiling & Linking lesson. The full lesson includes a quiz and a checked coding challenge with a free account.