2013-07-19 3 views
1

Я хорошо знаю boost::any и boost::variant, но в этом случае они не соответствуют моим потребностям.Реализация любого контейнера

Обычно, чтобы содержать объект неизвестного типа, можно получить его из общей базы и получить доступ к экземпляру с помощью виртуальных методов. Но что можно сделать, если невозможно использовать общую базу?

Я знаю, что в этом примере вам нужно будет знать тип, содержащийся в нем, но нести меня. std::vector - это шаблонный класс с классом верхнего уровня, также являющийся шаблоном. Насколько мне известно, не может быть предоставлена ​​база без шаблонов без изменения заголовков STL. Теперь давайте скажем, что я хочу создать вектор одного типа, но класс, содержащий класс, не заботится о типе, но он требует доступа к некоторым из «общих» методов, например size() и pop_back().

Используя boost::any, тип был удален, что делает невозможным разыменование содержащегося объекта. boost::variant и tuple требуют знать перед собой, какие типы могут быть вставлены, в результате чего содержащийся класс сам по себе является шаблоном.

То, что я до сих пор что-то вроде этого:

struct container 
{ 
    virtual ~container() = 0; 
    virtual void pop_back() = 0; 
    virtual size_t size() = 0; 
    ... 
} 
template < typename T > 
struct contained 
{ 
    std::vector<T> _vec; 
    contained (size_t n, T _what) : _vec(n, _what) {} 
    virtual void pop_back() { _vec.pop_back(); } 
    ... 
} 
class some_class 
{ 
    container* _cont; 
    template < typename T > 
    void create (T _first) { _cont = new contained<T>(1,_first); } 
    ... 
} 

Здесь клиент может позвонить по create() и параметр шаблона должен определяться автоматически. Не очень хороший пример, который я знаю, но я пытаюсь скрыть параметры шаблона от клиента. Не делая этого, some_class должен также отслеживать, какой тип хранится.

Мой метод основан на виртуальных вызовах, которые приводят к потере производительности, особенно если внутренний класс имеет свои собственные виртуальные методы.

Есть ли какие-либо другие типы контейнеров, которые лучше отвечали бы моим потребностям?

В идеале, я хотел бы что-то вроде этого

container = std::vector<T>; 
container.pop_back(); 
container.push_back(T2); // compile error if types don't match 

Где внутренне он будет отслеживать тип и сделать простой бросок, а не полагаться на виртуальные методы. Почти как auto с той разницей, что после объявления его тип может измениться.

Edit:

В действительности, я хочу, чтобы создать оболочку вокруг std::basic_filebuf. Этот класс-оболочка открывает файл с char, wchar_t или unsigned long на основе спецификации. Обертка также получена из basic_filebuf с параметром шаблона, который выбирается клиентом. Внутри он преобразует коды кодов Unicode из файла в желаемую кодировку клиентов. Проблема возникает при сохранении внутреннего basic_filebuf, поскольку он может быть объявлен любым типом в качестве параметра шаблона. Я не хочу использовать специализированную специализацию, потому что хочу, чтобы клиент мог передать свой собственный экземпляр basic_filebuf.

Должно быть совместимо с VS2010, которое имеет ограниченные возможности от C++ 11.

+0

Какая проблема вы пытаетесь решить, где именно это нужно? – GManNickG

+0

@GManNickG В качестве примера можно привести стандартные классы потоков, в которых тип элемента может быть 'char'' wchar_t' 'int' ... Как бы вы сохранили этот шаблон без заботы о внутреннем типе? Как насчет 'std :: string' и' std :: wstring'? Почти идентичны, но требуют немного другой обработки. – Twifty

+1

Извините, я не следую. Я имею в виду, что ваша программа будет в конечном итоге * делать *? – GManNickG

ответ

0

Это не может сочетаться с проверкой типа времени компиляции.Основываясь на Вашем «в идеале» Например:

container c; 
if (thingKnownAtRunTime()) { 
    c = vector<int>; 
} else { 
    c = vector<string>; 
} 
c.push_back("hello world"); 

Для случая filebuf, хотя, вы можете получить достаточно хорошо с чем-то вроде (предупреждение: непроверенной)

template<typename FILEBUF> 
void gensputc(FILEBUF* fb, long long c) { 
    FILEBUf::char_type c2 = smart_convert<FILEBUf::char_type>(c); 
    fb->sputc(c2); 
} 

class FileBufWrapper { 
public: 
    template<typename FILEBUF> FileBufWrapper(FILEBUF* fb) { 
    fb_ = fb; 
    sputc_ = gensputc<FILEBUF>; 
    } 
    void sputc(long long c) { 
    sputc_(fb_,c); 
    } 
private: 
    typedef void(*sputc_t)(void*, long long); 
    sputc_t sputc_; 
    void* fb_; 
}; 

smart_convert функция вызывает исключение во время выполнения, если значение не может быть преобразовано в тип char. Кроме того, это необходимо сделать для каждой функции, которую вы намереваетесь вызывать.

Если у вас есть доступ к std :: function и ++ std :: bind, то это может быть сделано немного чище, особенно для случаев, когда вам не нужно ничего конвертировать.

Смежные вопросы