Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Fixing the "Unresolved External" Linkage Error

Last updated Jan 1, 2003.

If you’re looking for more up-to-date information on this topic, please visit our C/C++ Programming article, podcast, and store pages.

In general, compilation errors are self-explanatory. Linkage errors however are vague and less pinpointed. One of the common linkage errors is "unresolved external symbol". It designates at least three different scenarios:

  • A freestanding function or a member function that you declared
  • A static data member of a class
  • A template function or a template member (i.e., a member function of a class template)

Here I will discuss each case in detail. First, I will show a typical program that causes this linkage error and then I will discuss the remedies.

Functions

Here the term function collectively refers to a freestanding extern function, a class member function and a static member function. When the said linkage error refers to one of these function types, it usually means that you have the following program structure:

A header file declares the function. The program calls that function, either directly or indirectly (e.g., by invoking an object’s constructor), but the definition of that function could not be found. The omission of the function definition is a result of a human error ( the programmer forgot to implement the function), or a configuration error -- the source file that contains the function definition wasn’t compiled for some reason. The following program leads to this linkage error:

//file c.h
class C
{
public:
 C(); 
void func() const; 
};
//file c.cpp
#include "c.h"
void C::func() const {cout<<"hello"<<endl;}
//constructor definition is missing
int main()
{
C c; //implicit constructor call 
c.func();
}

The compilation runs smoothly but the linker reports that it could not resolve the external symbol C::C(). In order to construct the object c, C++ must invoke C’s constructor. The compiler therefore inserts a constrictor invocation instruction into the object file. At link time, the linker tries to resolve this call by matching it against the constructor body. Because the programmer forgot to define the constructor, the linker can’t find the constructor body. Consequently, you get this error.

To fix this program, examine carefully the error message you’re getting. It should state which symbol can’t be resolved.

Static Data Members

Nonstatic data members of a class are defined inside their class body. In this respect, their declaration is also a definition because the compiler allocates storage for them. For example:

class A
{
private:
int m; //declaration and definition
};

Static data members are different. Although you must declare them inside the class body, you also need to define them outside the class body. This separation is a stumbling block for many novices because they don’t see why static data members require this special treatment.

When the program refers to a static data member that is only declared but not defined, the "unresolved external symbol" error occurs. Let’s look at a concrete example of this:

//file A,h
class A
{
private:
 static int m; //declaration only
public:
 static int get_m() {return m;}
};

//file main.cpp
#include "A.h"
int main()
{
A a;
a.get_m(); //Unresolved external ’A::m’

}

Remember: a declaration of a static data member doesn’t allocate storage for it nor does it initialize it. The definition is responsible for that. Where should the definition appear? Usually, in the corresponding .cpp file:

//file A.cpp
#include "A.h"
int A::m=5;//definition with an explicit initializer

There’s one exception to this rule: const static data members of an integral type. In this case, you can initialize (hence, define) the data member inside the class:

struct S
{
const static int size=100; //declaration and definition
};

Templates

The third cause of "unresolved external" error is templates. If judged by the number of questions posted on C++ forums, this is a common pitfall. First, let’s look at the symptoms. You declared a class template (or a standalone function template) in a header file. The definitions of the template’s member functions (or the standalone function template) are placed in a matching .cpp file. This separation between a header file that contains prototypes and a .cpp file that contains the functions’ bodies is based on the misconception that templates behave exactly as ordinary classes and functions with respect to the separation between their declarations and definitions. They don’t. Unlike an ordinary function or class, templates’ definitions must be accessible from every translation unit that uses them. In other words, a header file containing template declarations must also host their definitions.

First, let’s look at a program that demonstrates the problem:

//file "Ptr.h"
template <class T>
class Ptr
{
public:
 Ptr(T * p);
 ~Ptr();
 bool empty() const;
private:
 T *t;
};

int main()
{
 Ptr<int> ptr(new int); //unresolved external
}

The solution is to ensure that the definitions of the member functions are provided in the header file itself. This can be done by defining the member functions inline, or by adding out of line definitions after the class declaration. Both forms are shown here:

//file "Ptr.h"
template <class T>

class Ptr
{
T *t;
public:
 Ptr(T * p);
 ~Ptr() {delete p;} inline definition
 bool empty() const;
};

template <class T> Ptr<T>::Ptr (T * ptr) : p(ptr) {} //out of lne definition
template <class T> bool Ptr<T>::empty() const {return false;}

For programmers who have been used to split declarations of ordinary functions and classes from their definitions, this style seems unintuitive. However, there’s no need to worry about duplicate symbols. Linkers know to collapse multiple instances of the same template specialization so the resulting executable file doesn’t contain duplicates.

Now, the compilation and linkage of the following program run smoothly:

int main()
{
 Ptr<int> ptr(new int); //unresolved external
}

Summary

"Experience", that elusive sought-after quality that almost every job description requires, can be broken down into a long list of pinpointed skills and facts such as the ability to translate an obscure linkage error to specific coding steps.