2016-01-30 2 views
2

В настоящее время я пишу класс шаблона для архивирования (или сериализации) и распаковки данных в/из двоичного формата. Во-первых, я пытаюсь закрыть, какую модель я буду использовать. Я в основном склонен к использованию шаблонов, потому что у unarchivers нет типа ввода для перегрузки метода. Например, следующий пример OK:Парадигма относительно специализации класса шаблонов

Archiver ar; 
int i; 

archive(ar, i); 

Но это аналог не:

Unarchiver unar; 
int i; 

i = unarchive(unar); 

Я хотел бы избежать, используя имя функции, такие как unarchive_int, потому что это будет хлопотно при использовании шаблоны. Скажи:

template <class T> class SomeClass 
{ 
public: 

    void doSomething() 
    { 
     // Somewhere 
     T value = unarchive(unar); 
    } 
}; 

Это сделало бы вещи неаккуратно, и как таковой я, а на самом деле использовать шаблоны для этого, в то время как предыдущее выражение будет T value = unarchive<T>(ar);. Также кажется глупо (возможно) писать глобальную функцию, если либо первый, либо единственный параметр всегда являются объектами архиватора и unarchiver; класс шаблона, кажется, в порядке:

template <class T> class Archiver 
{ 
public: 

    void archive(T obj); 
}; 

Это работает, но метод архивации всегда копирует свой входной объект. Это нормально с типами данных POD, но не столько для классов. Решение кажется очевидным и вместо этого использует ссылку на константу как в void archive(const T & obj), но теперь также кажется глупым передавать целые числа, поплавки и другие POD по ссылке. Хотя я был бы доволен этим решением, я попытался пойти немного дальше и сделать объект вместо этого. Мой первый подход - std::enable_if, при этом предполагается, что по умолчанию используется копия (для всех членов, не относящихся к классу), и предоставлена ​​специализация класса, где метод archive получает свой ввод по ссылке вместо этого. Это не работает. Вот код:

template <class T, class E = void> 
class Archiver 
{ 
public: 

    // By default, obj is passed by copy 
    void archive(T obj); 
}; 

template <class T> 
class Archiver<T, typename std::enable_if<std::is_class<T>::value && !std::is_pod<T>::value>::value> 
{ 
public: 

    // I would expect this to be used instead if is_class<T> && !is_pod<T> 
    void archive(const T & obj); 
}; 

Проблема заключается в том, что вторая декларация не видна на всех компилятором, и вот доказательство:

template <> void Archiver<std::uint8_t>::archive(uint8_t obj); 
template <> void Archiver<std::string>::archive(const std::string & obj); 

Бывший компилируется нормально, но потом дает:

вне линии декларирование «архива» не соответствует ни одной декларации в 'Archiver<std::__1::basic_string<char>, void>'

С другой стороны, если я получу std::string вместо копии, если компилируется просто отлично. Я думаю, что я знаю, почему это происходит, компилятор выбирает первый шаблон, поскольку он достаточно общий для обеих деклараций, но как же я могу выбрать более специализированную версию?

+2

Обратите внимание, что вы можете передать некоторые типы POD в качестве ссылки. Просто потому, что это тип POD, это не значит, что быстро перейти к функции (в частности, если это большой тип POD). – Cornstalks

+0

@ Корнисторы да, я знаю. Это «первый подход», я сначала пытаюсь решить эту проблему с разрешением, тогда я буду рассматривать, какие типы передаются копией и которые передаются по ссылке;), но спасибо за головы! –

+0

Я откат вашего редактирования. Пожалуйста, не меняйте вопрос (и ваше редактирование, конечно, изменило бы вопрос), поскольку это превращает вопрос в движущуюся цель. Вместо этого откройте новый вопрос или добавьте дополнительный раздел к вопросу с новыми ошибками кода/новых. – Cornstalks

ответ

5

Вы хотите std::enable_if<...>::type, неstd::enable_if<...>::value.

Here's a full demo:

#include <type_traits> 
#include <cstdint> 
#include <string> 

template <class T, class E = void> 
struct Archiver { 
    void archive(T obj); 
}; 

template <class T> 
struct Archiver<T, typename std::enable_if<std::is_class<T>::value && !std::is_pod<T>::value>::type> 
{ 
    void archive(const T & obj); 
}; 

template <> void Archiver<std::uint8_t>::archive(std::uint8_t obj); 
template <> void Archiver<std::string>::archive(const std::string & obj); 
+0

Gotcha. Вы правы в этом, но он все еще не компилируется. Я отредактирую вопрос. –

+0

@ AndréFratelli: Он компилируется для меня хорошо (мне пришлось изменить 'uint8_t obj' на' std :: uint8_t obj'). – Cornstalks

+0

Подождите, это сработало! Я передавал струну копией, так или иначе я оставил ее так же, как и во время моих экспериментальных итераций: P Спасибо! –

1

IIUC, вопрос сводится к тому, как определить общий тип шаблона, который оптимизирован для вызова функций.

Для этого, вы можете рассмотреть boost::call_traits, в частности, param_type:

template<typename T> 
void foo(typename boost::call_traits<T>::param_type t); 
+0

Благодарим вас за ответ, но я не использую boost. Ответы кукурузных полов уже сделали это для меня. В любом случае, спасибо :) –

+0

Добро пожаловать. Удачи вам в архиваторе! –

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