![](/img/trans.png)
[英]Non-template class with a template class as a field without specifying template parameters
[英]Templates without T parameters in a non-template class
好的,关于这个问题,我到处都读到了很多答案,但是可能由于我不知道正确的语法,所以我不知道该怎么做。
我有一个非模板类,该类必须实现不同的静态实用程序功能,主要用于序列化和反序列化。 我目前拥有的是这样的:
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);
现在,我尝试了不同的方法,但是由于所有功能我只看到自动实现模板的示例,而一次却看不到一个重载,因此我不知道如何实现此功能。
当然,这是带有QT的C ++。
如评论中所述,它称为模板专门化,如下所示:
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
时需要添加template参数。
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;
}
但是,正如我已经说过的,我永远不会采用该解决方案。 相反,我将对这些各种类型使用简单的函数重载。
为了避免多次编写相同的内容,我将定义一个包含类逻辑的独特帮助器函数(私有方法)。
例如,辅助函数可以创建一个普通的内存缓冲区,用于在其中序列化类(char数组),然后重载函数应仅在适当的输入数据结构中适应该数组。
这样,当类逻辑(例如,数据成员)更改时,您应该仅修改帮助程序功能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.