прыщ для шаблонного класса - PullRequest
15 голосов
/ 22 октября 2011

Я хочу использовать идиому pimpl, чтобы пользователи моей библиотеки не нуждались в наших внешних зависимостях (например, boost и т. Д.), Однако, когда мой класс является шаблонным, это кажется невозможным, потому что методы должны быть в заголовке. Есть ли что-то, что я могу сделать вместо этого?

Ответы [ 3 ]

9 голосов
/ 22 октября 2011

Если класс является шаблонным, ваши пользователи, по сути, должны его скомпилировать (и это буквально верно в наиболее широко используемых реализациях C ++), и поэтому им нужны ваши внешние зависимости.

Самое простое решение - поместить основную часть реализации вашего класса в не шаблонный базовый класс (или инкапсулированный объект-член некоторого класса). Решите проблему скрытия модулей там.

А затем напишите производный от шаблона (или включающий) класс, чтобы добавить к нему безопасность типов.

Например, предположим, что у вас есть шаблон, который предоставляет удивительную возможность выделять при первом доступе (, опуская необходимый конструктор копирования, назначение, деструктор ):

template <class T>
class MyContainer
{
    T *instance_;

public:
    MyContainer() : instance_(0) {}

    T &access()
    {
        if (instance_ == 0)
            instance_ = new T();

        return *instance_;
    }
};

Если вы хотите, чтобы «логика» была разделена на базовый класс, не связанный с шаблоном, вам нужно было бы параметризовать поведение не шаблонным способом, то есть использовать виртуальные функции:

class MyBase
{
    void *instance_;

    virtual void *allocate() = 0;

public:
    MyBase() : instance_(0) {}

    void *access()
    {
        if (instance_ == 0)
            instance_ = allocate();

        return instance_;
    }
};

Затем вы можете добавить понимание типа во внешний слой:

template <class T>
class MyContainer : MyBase
{
    virtual void *allocate()
        { return new T(); }

public:
    T &access()
        { return *(reinterpret_cast<T *>(MyBase::access())); }
};

т.е. Вы используете виртуальные функции, чтобы шаблон мог «заполнять» зависящие от типа операции. Очевидно, что этот шаблон действительно имеет смысл, только если у вас есть бизнес-логика, которую стоит скрыть.

2 голосов
/ 22 октября 2011

Вы можете явно создавать шаблоны в исходном файле, но это возможно только в том случае, если вы знаете, какой будет тип шаблона.В противном случае не используйте идиомы pimpl для шаблонов.

Примерно так:

header.hpp:

#ifndef HEADER_HPP
#define HEADER_HPP

template< typename T >
class A
{
  // constructor+methods + pimpl
};

#endif

source.cpp:

#include "header.hpp"

// implementation

// explicitly instantiate for types that will be used
template class A< int >;
template class A< float >;
// etc...
1 голос
/ 22 октября 2011

Существует два основных решения:

  • , в то время как интерфейс зависит от некоторого типа T, он относится к более слабо типизированной реализации (например, одно, использующее указатели void* напрямую илистирание типа корыта), или

  • вы поддерживаете только определенное и довольно ограниченное количество типов.

Второе решение относится, например, к char / wchar_t -зависимые вещи.

Первое решение было довольно распространенным в первые дни шаблонов C ++, потому что в то время компиляторы не умели распознавать общие черты в сгенерированном машинном коде и вводили бытак называемый «раздувание кода».Сегодня, к большому удивлению любого новичка, который пробует его, шаблонное решение часто может иметь меньший объем машинного кода, чем решение, основанное на полиморфизме во время выполнения.Конечно, YMMV.

Приветствия & hth.,

...