在递归模板调用中无法调用模板函数重载
Templated function overload is failing to be called in recursive template call
我正在 运行 遇到一个问题,即为某些模板类型执行适当的函数模板重载。下面显示了我 运行 正在研究的内容所需的最小示例:
#include <cstdio>
#include <vector>
template<typename id_type>
struct B {
id_type ID;
std::vector<int> values;
};
template<typename id_type>
struct A {
id_type ID;
std::vector<struct B<id_type>> b_elems;
};
// forward declarations
namespace aSDG {
namespace meshing {
template<typename id_type> size_t byte_content(const struct B<id_type>& instance);
template<typename id_type> size_t serialize(const struct B<id_type>& instance, unsigned char* buffer, size_t start_idx = 0);
template<typename id_type> size_t deserialize(struct B<id_type>& instance, const unsigned char* buffer, size_t start_idx = 0);
template<typename id_type> size_t byte_content(const struct A<id_type>& instance);
template<typename id_type> size_t serialize(const struct A<id_type>& instance, unsigned char* buffer, size_t start_idx = 0);
template<typename id_type> size_t deserialize(struct A<id_type>& instance, const unsigned char* buffer, size_t start_idx = 0);
}
}
namespace aSDG {
namespace meshing {
// serialization for primitive types
template<typename T> size_t byte_content(const T& data){
return sizeof(T);
}
template<typename T> size_t serialize(const T& data, unsigned char* buffer, size_t start_idx = 0)
{
std::memcpy((void*)(buffer + start_idx), (void*)&data, sizeof(data));
return start_idx + sizeof(data);
}
template<typename T> size_t deserialize(T& data, const unsigned char* buffer, size_t start_idx = 0)
{
std::memcpy((void*)&data, (void*)(buffer + start_idx), sizeof(data));
return start_idx + sizeof(data);
}
// serialization for vector containers
template<typename T> size_t byte_content(const std::vector<T>& data){
// get number of bytes for the size variable
size_t num_req_bytes = sizeof(size_t);
// get the number of bytes for each element of the vector
for(size_t i = 0; i < data.size(); ++i){
num_req_bytes += byte_content(data[i]);
}// end for i
// return the total number of required bytes
return num_req_bytes;
}
template<typename T> size_t serialize(const std::vector<T>& data, unsigned char* buffer, size_t start_idx = 0)
{
// add the number of elements in the data
const size_t size_ = data.size();
start_idx = serialize(size_, buffer, start_idx);
// add the actual data elements
for(size_t i = 0; i < size_; ++i){
start_idx = serialize(data[i], buffer, start_idx);
}// end for i
// return the final index after adding all the data
return start_idx;
}
template<typename T> size_t deserialize(std::vector<T>& data, const unsigned char* buffer, size_t start_idx = 0)
{
// get the number of elements in the array
size_t size_ = 0;
start_idx = deserialize(size_, buffer, start_idx);
// resize the input array
data.resize(size_);
// fill the array with the data in the buffer
for(size_t i = 0; i < size_; ++i){
start_idx = deserialize(data[i], buffer, start_idx);
}// end for i
// return the number of bytes we are at in the array
return start_idx;
}
} // end namespace meshing
} // end namespace aSDG
namespace aSDG {
namespace meshing {
// serialization for B
template<typename id_type>
size_t byte_content(const struct B<id_type>& instance) {
return byte_content(instance.ID) + byte_content(instance.values);
}
template<typename id_type>
size_t serialize(const struct B<id_type>& instance, unsigned char* buffer, size_t start_idx){
start_idx = serialize(instance.ID, buffer, start_idx);
return serialize(instance.values, buffer, start_idx);
}
template<typename id_type>
size_t deserialize(struct B<id_type>& instance, const unsigned char* buffer, size_t start_idx){
start_idx = deserialize(instance.ID, buffer, start_idx);
return deserialize(instance.values, buffer, start_idx);
}
// serialization functions for A
template<typename id_type>
size_t byte_content(const struct A<id_type>& instance) {
return byte_content(instance.ID) + byte_content(instance.b_elems);
}
template<typename id_type>
size_t serialize(const struct A<id_type>& instance, unsigned char* buffer, size_t start_idx){
start_idx = serialize(instance.ID, buffer, start_idx);
return serialize(instance.b_elems, buffer, start_idx);
}
template<typename id_type>
size_t deserialize(struct A<id_type>& instance, const unsigned char* buffer, size_t start_idx){
start_idx = deserialize(instance.ID, buffer, start_idx);
return deserialize(instance.b_elems, buffer, start_idx);
}
} // end namespace meshing
} // end namespace aSDG
int main(int argc, const char * argv[]) {
struct A<size_t> a1, a2;
a1.b_elems.emplace_back();
a1.b_elems.emplace_back();
a1.b_elems.emplace_back();
a1.b_elems[0].ID = 5;
a1.b_elems[0].values.push_back(1);
// get the number of bytes to be serialized
size_t num_req_bytes = aSDG::meshing::byte_content(a1);
// allocate the buffer
std::vector<unsigned char> buf( num_req_bytes );
// serialize the data in a1
size_t serial_bytes = aSDG::meshing::serialize(a1, &buf[0]);
// deserialize data into a2
size_t deserial_bytes= aSDG::meshing::deserialize(a2, &buf[0]);
// check that the bytes match
printf("in_bytes = %zu vs. out_bytes = %zu\n", serial_bytes, deserial_bytes );
return 0;
}
在这个例子中,我将序列化 A
类型的一个实例,而这个序列化又需要序列化包含在 A
中的 B
个元素的向量。 A
运行 的所有序列化函数,意味着它的 byte_content
、serialize
和 deserialize
的风格都使用适当的定义调用。但是,当程序递归到这些方法的通用 std::vector
定义以序列化 A
的 std::vector<struct B>
数据成员时,它无法调用为 B
定义的方法,并且而是为基本原语(代码示例顶部定义的前三个)调用序列化函数。我不明白为什么在这种情况下没有调用 B
的序列化方法(byte_content
、serialize
、deserialize
),因为它们已定义。
我怀疑我遗漏了一些关于如何选择函数模板重载的基本规则,但我真的不确定。任何见解将不胜感激。
编辑 1
更准确的说,关键问题是当A
的序列化发生时,它实际上会调用下面预期的方法
template<typename id_type>
size_t aSDG::meshing::serialize(const struct A<id_type>& instance, unsigned char* buffer, size_t start_idx = 0){
start_idx = serialize(instance.ID, buffer, start_idx);
return serialize(instance.b_elems, buffer, start_idx);
}
问题是,当它去序列化 b_elems
时,它首先使用 T = struct B
调用通用 std::vector
序列化方法
template<typename T> size_t serialize(const std::vector<T>& data, unsigned char* buffer, size_t start_idx = 0)
{
// add the number of elements in the data
const size_t size_ = data.size();
start_idx = serialize(size_, buffer, start_idx);
// add the actual data elements
for(size_t i = 0; i < size_; ++i){
start_idx = serialize(data[i], buffer, start_idx);
}// end for i
// return the final index after adding all the data
return start_idx;
}
但是当它执行 serialize(data[i], buffer, start_idx)
时,该函数不会调用
template<typename id_type>
size_t serialize(const struct B<id_type>& instance, unsigned char* buffer, size_t start_idx = 0){
start_idx = serialize(instance.ID, buffer, start_idx);
return serialize(instance.values, buffer, start_idx);
}
而是调用
template<typename T> size_t serialize(const T& data, unsigned char* buffer, size_t start_idx = 0)
{
std::memcpy((void*)(buffer + start_idx), (void*)&data, sizeof(data));
return start_idx + sizeof(data);
}
我真的很困惑为什么会这样。
编辑 2
添加@Evg 推荐的前向声明后,代码几乎可以按我的预期运行。现在唯一的问题是没有调用 B
的 byte_content
专业化。可以通过将 B
的上述专业化定义替换为
来验证这一点
template<typename id_type>
size_t byte_content(const struct B<id_type>& instance) {
printf("B byte_content\n");
return byte_content(instance.ID) + byte_content(instance.values);
}
template<typename id_type>
size_t serialize(const struct B<id_type>& instance, unsigned char* buffer, size_t start_idx){
printf("B serialize\n");
start_idx = serialize(instance.ID, buffer, start_idx);
return serialize(instance.values, buffer, start_idx);
}
template<typename id_type>
size_t deserialize(struct B<id_type>& instance, const unsigned char* buffer, size_t start_idx){
printf("B deserialize\n");
start_idx = deserialize(instance.ID, buffer, start_idx);
return deserialize(instance.values, buffer, start_idx);
}
并见证 "B byte_content" 消息从未显示。现在也许我只是累了,没有看到一些错误,但我不明白为什么即使在前向声明之后,也没有调用 B
的正确 byte_content
专业化。
注意:此答案指的是编辑前的问题(无前向声明)。
在 serialize(const std::vector<T>& data...)
中,您使用了不合格的名称 serialize
。编译器应该决定调用哪个 serialize
。它将考虑功能 1) 在定义时可见的功能和 2) 在实例化时可由 ADL 找到的功能。两次查找都找不到 serialize(const B<id_type>&...)
.
一个可能的解决方案是提出声明
template<typename id_type>
size_t byte_content(const B<id_type>&);
template<typename id_type>
size_t serialize(const B<id_type>&, unsigned char*, size_t = 0);
template<typename id_type>
size_t deserialize(B<id_type>&, const unsigned char*, size_t = 0);
一开始。
我正在 运行 遇到一个问题,即为某些模板类型执行适当的函数模板重载。下面显示了我 运行 正在研究的内容所需的最小示例:
#include <cstdio>
#include <vector>
template<typename id_type>
struct B {
id_type ID;
std::vector<int> values;
};
template<typename id_type>
struct A {
id_type ID;
std::vector<struct B<id_type>> b_elems;
};
// forward declarations
namespace aSDG {
namespace meshing {
template<typename id_type> size_t byte_content(const struct B<id_type>& instance);
template<typename id_type> size_t serialize(const struct B<id_type>& instance, unsigned char* buffer, size_t start_idx = 0);
template<typename id_type> size_t deserialize(struct B<id_type>& instance, const unsigned char* buffer, size_t start_idx = 0);
template<typename id_type> size_t byte_content(const struct A<id_type>& instance);
template<typename id_type> size_t serialize(const struct A<id_type>& instance, unsigned char* buffer, size_t start_idx = 0);
template<typename id_type> size_t deserialize(struct A<id_type>& instance, const unsigned char* buffer, size_t start_idx = 0);
}
}
namespace aSDG {
namespace meshing {
// serialization for primitive types
template<typename T> size_t byte_content(const T& data){
return sizeof(T);
}
template<typename T> size_t serialize(const T& data, unsigned char* buffer, size_t start_idx = 0)
{
std::memcpy((void*)(buffer + start_idx), (void*)&data, sizeof(data));
return start_idx + sizeof(data);
}
template<typename T> size_t deserialize(T& data, const unsigned char* buffer, size_t start_idx = 0)
{
std::memcpy((void*)&data, (void*)(buffer + start_idx), sizeof(data));
return start_idx + sizeof(data);
}
// serialization for vector containers
template<typename T> size_t byte_content(const std::vector<T>& data){
// get number of bytes for the size variable
size_t num_req_bytes = sizeof(size_t);
// get the number of bytes for each element of the vector
for(size_t i = 0; i < data.size(); ++i){
num_req_bytes += byte_content(data[i]);
}// end for i
// return the total number of required bytes
return num_req_bytes;
}
template<typename T> size_t serialize(const std::vector<T>& data, unsigned char* buffer, size_t start_idx = 0)
{
// add the number of elements in the data
const size_t size_ = data.size();
start_idx = serialize(size_, buffer, start_idx);
// add the actual data elements
for(size_t i = 0; i < size_; ++i){
start_idx = serialize(data[i], buffer, start_idx);
}// end for i
// return the final index after adding all the data
return start_idx;
}
template<typename T> size_t deserialize(std::vector<T>& data, const unsigned char* buffer, size_t start_idx = 0)
{
// get the number of elements in the array
size_t size_ = 0;
start_idx = deserialize(size_, buffer, start_idx);
// resize the input array
data.resize(size_);
// fill the array with the data in the buffer
for(size_t i = 0; i < size_; ++i){
start_idx = deserialize(data[i], buffer, start_idx);
}// end for i
// return the number of bytes we are at in the array
return start_idx;
}
} // end namespace meshing
} // end namespace aSDG
namespace aSDG {
namespace meshing {
// serialization for B
template<typename id_type>
size_t byte_content(const struct B<id_type>& instance) {
return byte_content(instance.ID) + byte_content(instance.values);
}
template<typename id_type>
size_t serialize(const struct B<id_type>& instance, unsigned char* buffer, size_t start_idx){
start_idx = serialize(instance.ID, buffer, start_idx);
return serialize(instance.values, buffer, start_idx);
}
template<typename id_type>
size_t deserialize(struct B<id_type>& instance, const unsigned char* buffer, size_t start_idx){
start_idx = deserialize(instance.ID, buffer, start_idx);
return deserialize(instance.values, buffer, start_idx);
}
// serialization functions for A
template<typename id_type>
size_t byte_content(const struct A<id_type>& instance) {
return byte_content(instance.ID) + byte_content(instance.b_elems);
}
template<typename id_type>
size_t serialize(const struct A<id_type>& instance, unsigned char* buffer, size_t start_idx){
start_idx = serialize(instance.ID, buffer, start_idx);
return serialize(instance.b_elems, buffer, start_idx);
}
template<typename id_type>
size_t deserialize(struct A<id_type>& instance, const unsigned char* buffer, size_t start_idx){
start_idx = deserialize(instance.ID, buffer, start_idx);
return deserialize(instance.b_elems, buffer, start_idx);
}
} // end namespace meshing
} // end namespace aSDG
int main(int argc, const char * argv[]) {
struct A<size_t> a1, a2;
a1.b_elems.emplace_back();
a1.b_elems.emplace_back();
a1.b_elems.emplace_back();
a1.b_elems[0].ID = 5;
a1.b_elems[0].values.push_back(1);
// get the number of bytes to be serialized
size_t num_req_bytes = aSDG::meshing::byte_content(a1);
// allocate the buffer
std::vector<unsigned char> buf( num_req_bytes );
// serialize the data in a1
size_t serial_bytes = aSDG::meshing::serialize(a1, &buf[0]);
// deserialize data into a2
size_t deserial_bytes= aSDG::meshing::deserialize(a2, &buf[0]);
// check that the bytes match
printf("in_bytes = %zu vs. out_bytes = %zu\n", serial_bytes, deserial_bytes );
return 0;
}
在这个例子中,我将序列化 A
类型的一个实例,而这个序列化又需要序列化包含在 A
中的 B
个元素的向量。 A
运行 的所有序列化函数,意味着它的 byte_content
、serialize
和 deserialize
的风格都使用适当的定义调用。但是,当程序递归到这些方法的通用 std::vector
定义以序列化 A
的 std::vector<struct B>
数据成员时,它无法调用为 B
定义的方法,并且而是为基本原语(代码示例顶部定义的前三个)调用序列化函数。我不明白为什么在这种情况下没有调用 B
的序列化方法(byte_content
、serialize
、deserialize
),因为它们已定义。
我怀疑我遗漏了一些关于如何选择函数模板重载的基本规则,但我真的不确定。任何见解将不胜感激。
编辑 1
更准确的说,关键问题是当A
的序列化发生时,它实际上会调用下面预期的方法
template<typename id_type>
size_t aSDG::meshing::serialize(const struct A<id_type>& instance, unsigned char* buffer, size_t start_idx = 0){
start_idx = serialize(instance.ID, buffer, start_idx);
return serialize(instance.b_elems, buffer, start_idx);
}
问题是,当它去序列化 b_elems
时,它首先使用 T = struct B
std::vector
序列化方法
template<typename T> size_t serialize(const std::vector<T>& data, unsigned char* buffer, size_t start_idx = 0)
{
// add the number of elements in the data
const size_t size_ = data.size();
start_idx = serialize(size_, buffer, start_idx);
// add the actual data elements
for(size_t i = 0; i < size_; ++i){
start_idx = serialize(data[i], buffer, start_idx);
}// end for i
// return the final index after adding all the data
return start_idx;
}
但是当它执行 serialize(data[i], buffer, start_idx)
时,该函数不会调用
template<typename id_type>
size_t serialize(const struct B<id_type>& instance, unsigned char* buffer, size_t start_idx = 0){
start_idx = serialize(instance.ID, buffer, start_idx);
return serialize(instance.values, buffer, start_idx);
}
而是调用
template<typename T> size_t serialize(const T& data, unsigned char* buffer, size_t start_idx = 0)
{
std::memcpy((void*)(buffer + start_idx), (void*)&data, sizeof(data));
return start_idx + sizeof(data);
}
我真的很困惑为什么会这样。
编辑 2
添加@Evg 推荐的前向声明后,代码几乎可以按我的预期运行。现在唯一的问题是没有调用 B
的 byte_content
专业化。可以通过将 B
的上述专业化定义替换为
template<typename id_type>
size_t byte_content(const struct B<id_type>& instance) {
printf("B byte_content\n");
return byte_content(instance.ID) + byte_content(instance.values);
}
template<typename id_type>
size_t serialize(const struct B<id_type>& instance, unsigned char* buffer, size_t start_idx){
printf("B serialize\n");
start_idx = serialize(instance.ID, buffer, start_idx);
return serialize(instance.values, buffer, start_idx);
}
template<typename id_type>
size_t deserialize(struct B<id_type>& instance, const unsigned char* buffer, size_t start_idx){
printf("B deserialize\n");
start_idx = deserialize(instance.ID, buffer, start_idx);
return deserialize(instance.values, buffer, start_idx);
}
并见证 "B byte_content" 消息从未显示。现在也许我只是累了,没有看到一些错误,但我不明白为什么即使在前向声明之后,也没有调用 B
的正确 byte_content
专业化。
注意:此答案指的是编辑前的问题(无前向声明)。
在 serialize(const std::vector<T>& data...)
中,您使用了不合格的名称 serialize
。编译器应该决定调用哪个 serialize
。它将考虑功能 1) 在定义时可见的功能和 2) 在实例化时可由 ADL 找到的功能。两次查找都找不到 serialize(const B<id_type>&...)
.
一个可能的解决方案是提出声明
template<typename id_type>
size_t byte_content(const B<id_type>&);
template<typename id_type>
size_t serialize(const B<id_type>&, unsigned char*, size_t = 0);
template<typename id_type>
size_t deserialize(B<id_type>&, const unsigned char*, size_t = 0);
一开始。