2016-09-05 2 views
2

Хорошо, я прочитал много ответов здесь и там об этой проблеме, но, вероятно, так как я не знаю правильного синтаксиса, я не могу понять, как это сделать.Шаблоны без параметров T в классе без шаблона

У меня есть класс без шаблонов, который должен реализовывать различные статические служебные функции, в основном для сериализации и десериализации. То, что я в настоящее время есть что-то вроде этого:

class Data_Base : public QObject 
{ 
    ... 
protected: 
    static QByteArray Serialize(int value); 
    static int DeserializeInt(QByteArray ser); 
    static QByteArray Serialize(char *value); 
    static char *DeserializeCharArr(QByteArray ser); 
    static QByteArray Serialize(QString value); 
    static QString DeserializeQString(QByteArray ser); 
    .... 
} 

Теперь, я бы предпочел, чтобы все функции Deserialize* в качестве шаблона, так как это будет лучше. И в качестве бонуса также есть функции Serialize в качестве шаблонов, поэтому я заставил пользователя на самом деле явно сказать, какую перегрузку вызывать. Нечто, что может быть использован следующим образом:

QByteArray ba = Serialize<int>(5); 
... 
int theValue = Deserialize<int>(ba); 

Теперь, я пробовал разные подходы, но так как все функции, я видел только примеры, реализующие шаблоны автоматически и не одну перегрузки в то время я не мог выяснить, как сделать эту работу.

Конечно, это C++, с дополнениями QT.

+3

Термин, который вы, похоже, ищете, - это специализация *, а не перегрузка. Сделайте поиск по специализации * C++ template *. –

+0

Шаблоны полезны, когда код одинаковый для каждого типа. Если ваши функции отличаются друг от друга в соответствии со структурой данных, оптимальная перегрузка умных функций - лучшее решение. –

+1

Если речь идет о централизации вещей, так что изменение чего-то в одном меняет его вообще, как насчет того, чтобы они вызывали функцию extern? static QByteArray Serialize (int value) {return extern_serialize (value);} для всех из них, вероятно, в сочетании с тем, чтобы сделать функцию extern другом. – Aziuth

ответ

0

Как указано в комментариях, это называется специализацией шаблона и выглядит следующим образом:

class X 
{ 
    public: 
    template<typename T> 
    static QByteArray Serialize(T const& t); 

    template<typename T> 
    static T Deserialize(QByteArray& v); 
}; 

template<> 
QByteArray X::Serialize(int const& t) 
{ 
    /* ... */ 
} 

template<> 
QByteArray X::Serialize(QString const& t) 
{ 
    /* ... */ 
} 

template<> 
int X::Deserialize(QByteArray& v) 
{ 
    /* ... */ 
} 

template<> 
QString X::Deserialize(QByteArray& v) 
{ 
    /* ... */ 
} 

QByteArray x=X::Serialize(5); 
int y=X::Deserialize<int>(x); 

При использовании Serialize вам не нужно указывать параметр шаблона, поскольку он может быть выведен из типа аргумента.

Но вы не можете вывести по типу возврата, поэтому вам нужно добавить параметр шаблона при использовании Deserialize.

+0

Спасибо. Я уже пробовал это решение перед публикацией, но если я попытаюсь написать этот код внутри класса, компилятор говорит «ошибка: явная специализация в пространстве без имен». Означает ли это, что вы не можете написать функцию шаблона в определении класса? – frarugi87

+0

@ frarugi87 Я отредактировал ответ, используя класс. Вы, вероятно, ставите специализацию внутри определения класса. Он должен быть снаружи, определение класса содержит только объявление прототипа. – TFM

+0

Это похоже на работу. Единственное, что нужно сделать при разбиении объявления и определении в заголовке и исходном файле, - это добавить в конец файла заголовка 'template <> QByteArray X :: Serialize (значение int);' (и т. Д.), Вне определение класса. Спасибо! – frarugi87

0

IMO заставлять ваше решение использовать специализированную специализацию может быть плохой дизайн.

Как я уже сказал в комментарии, шаблоны, как правило, хороши, когда структура кода одинакова для каждого типа данных.

Сериализация - это деликатная операция (кастинг, необработанная память и т. Д.), А структура данных может определять разные неявные преобразования и создавать UB.


Если бы я должен был реализовать «шаблон» поведение, это будет первое решение (просто царапина!):

struct Foo { 
    // Some data member variables. 
    std::string m_nopod; 

    // Serialize data object. 'It' must to be a output iterator 
    template<typename It> 
    void serialize(It out_iterator) { 
    constexpr size_t BYTES_FOR_SIZE = sizeof(decltype(m_nopod.size())); 
    constexpr size_t BYTES_FOR_CHAR = sizeof(decltype(m_nopod)::value_type); 

    // size definitions. 
    const auto len_str = m_nopod.size(); 
    const auto len_data = BYTES_FOR_CHAR * len_str; 

    // Temporary memory buffers. 
    uint8_t memory_size[BYTES_FOR_SIZE]; 
    auto memory_data = std::make_unique<uint8_t[]>(len_data); 

    // Raw bytes copy. 
    std::memcpy(memory_size, &len_str, BYTES_FOR_SIZE); 
    std::memcpy(memory_data.get(), m_nopod.data(), len_data); 

    // write with the iterator. 
    for (size_t i = 0; i < BYTES_FOR_SIZE; ++i) { 
     *out_iterator = memory_size[i]; 
    } 

    for (size_t i = 0; i < len_data; ++i) { 
     *out_iterator = memory_data[i]; 
    } 
    } 
}; 

Где out_iterator обязательно быть output_iterator, и ::value_type должен быть неявным конвертируемым типом до unsigned char.

Функция может быть вызвана с различными структурами данных (контейнерами):

int main(int argc, char *argv[]) { 
    std::vector<char> memory_buffer_char; 
    std::vector<int> memory_buffer_int; 
    std::string memory_buffer_str; 

    Foo foo{"a_string"}; 

    foo.serialize(std::back_inserter(memory_buffer_char)); 
    foo.serialize(std::back_inserter(memory_buffer_int)); 
    foo.serialize(std::back_inserter(memory_buffer_str)); 
    return 0; 
} 

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

Чтобы не писать одну и ту же вещь более одного раза, я определю уникальную вспомогательную функцию (частный метод), которая содержит логику класса.

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

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

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