Поскольку в C++ однопроходный компилятор, и для увязки единиц компиляции используются заголовочные файлы, содержащие объявление всех нижеиспользуемых абстрактных типов данных и функций, в C++ есть некоторые специфические приёмы программирования.
Одним из них является PImpl (pointer to implementation) - способ и скрыть от пользователя особенности реализации того или иного класса, и, что гораздо важнее, хоть как-то упорядочить декларационные зависимости между элементами, не перегружая заголовочный файл и пространство имён всякими "потрохами" классов.
Идея предельно простая:
В заголовочном файле, допустим, header.h, пишем
#ifndef HEADER_H
#define HEADER_H
class C
{
private:
    class Implementation;
    Implementation *implementation;
public:
    C();
    void f();
    ~C();
};
#endif
здесь только продекларировано существование класса Implementation, и поскольку нет обращений к его членам, а есть только указатель (размер которого в памяти не зависит от типа), компилятор это успешно переваривает.
В файле реализации, допустим, code.cpp, пишем
#include<iostream>
#include "header.h"
using namespace std;
class C::Implementation
{
public:
    void f()
    {
        wcout << L"Hello world" << endl;
    }
};
C::C() :
    implementation(NULL)
{
    this->implementation = new C::Implementation();
}
void C::f()
{
    this->implementation->f();
}
C::~C()
{
    delete this->implementation
}
Разумеется, преимущества, связанные с полной инкапсуляцией реализации класса от пользователя, оборачиваются недостатком обязательного создания объекта в куче и рисками утечек памяти при некорректном завершении жизни объекта, а также дополнительными вызовами обёрточных функций. Ну и ко всему прочему приходится писать дополнительный обёрточный код, который всегда увеличивает трудозатраты на внесение изменений (по сравнению с Java и C#).
В вызывающем коде класс C выглядит и используется обычным образом
#include "header.h"
int main()
{
    C c;
    c.f(); // Hello world.
    return 0;
}
Положение становится скверным, когда класс является параметризируемым - шаблоном (template) в терминах C++. Код такого класса не компилируется до подстановки параметров, и для разных параметров компилятор генерирует разные реализации параметризированного класса. По этой причине весь код реализации шаблона обязан находиться в заголовочном файле, чтобы компилятор в момент определения всех параметров мог сгенерировать новый класс по шаблону (для старых стандартов до C++11).
#ifndef HEADER_H
#define HEADER_H
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class C
{
public:
    void Process(T &item)
    {
        wcout << item << endl;
    }
};
#endif
На первый взгляд это с PImpl несовместимо. Однако в C++ есть ещё и специализации шаблонов: заранее определённые программистом версии параметризированного класса с конкретными значениями параметров.
template<>
class C<wstring>
{
public:
    void Process(wstring &item)
    {
        wcout << item << endl;
    }
};
Поэтому в том частном случае, когда набор значений параметров шаблонов заранее известен, можно добиться комбинации шаблонов и PImpl.
Заголовочный файл header.h
#ifndef HEADER_H
#define HEADER_H
#include <cstdlib>
template<typename T>
class Class
{
private:
        class Implementation;
        Implementation *implementation;
public:
        Class();
        void Process(T &item);
        ~Class();
};
#endif
Здесь мы определяем шаблон класса C, но реализация его методов будет специализированной под конкретные типы, и поэтому может быть вынесена из заголовочного файла. Для всех типов, не входящих в перечень специализаций, будет возникать ошибка компиляции. Внутри определяем класс Implementation, чтобы на не него распространялось действие параметра T, поскольку Implementation - тоже элемент специализации. Определение снаружи потребует template-выражения.
Файл реализации code.cpp
#include <iostream>
#include <string>
#include "header.h"
using namespace std;
template<typename T>
class Class<T>::Implementation
{
public:
        void Process(T &item)
        {
                wcout << item << endl;
        }
};
#define SPECIALIZATION(T)                                                       \
                                                                                \
        Class<T>::Class() :                                                         \
                implementation(NULL)                                                    \
        {                                                                           \
                this->implementation = new Class<T>::Implementation();                  \
        }                                                                           \
                                                                                \
        void Class<T>::Process(T &item)                                             \
        {                                                                           \
                this->implementation->Process(item);                                    \
        }                                                                           \
                                                                                \
        Class<T>::~Class()                                                          \
        {                                                                           \
                delete this->implementation;                                            \
        }                                                                           \
SPECIALIZATION(wstring)
SPECIALIZATION(int)
SPECIALIZATION(double)
#undef SPECIALIZATION
Здесь мы вынуждены реализовать все варианты каждого метода класса C, чтобы получились специализации для каждого типа. Чтобы не заниматься copy-paste вручную, разумно использовать макрос. А класс Implementation мы можем оформить в виде шаблона, вызываемого из каждой специализации - для каждой специализации будет автоматически создан собственный вариант.
Код вызывающей программы по-прежнему прост и обыкновенен:
#include <iostream>
#include <string>
#include "header.h"
using namespace std;
int main()
{
        {
                wstring x(L"Hello world");
                Class<wstring> object;
                object.Process(x);
        }
        {
                double x = 3.14159;
                Class<double> object;
                object.Process(x);
        }
        /* Тут будет ошибка - нет специализации для float.
        {
                float x = 3.14159;
                Class<float> object;
                object.Process(x);
        }
        */
        wcin.get();
        return 0;
}