在递归模板调用中无法调用模板函数重载

时间:2019-06-21 06:04:18

标签: c++ function templates overloading

我遇到了要对某些模板化类型执行适当的函数模板重载的问题。下面显示了查看我正在遇到的内容所需的最小示例:

#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的实例,而该序列化又需要序列化B中包含的A元素的向量。 A的所有序列化函数都运行,这意味着将使用适当的定义调用byte_contentserializedeserialize的味道。但是,当程序递归到这些方法的通用std::vector定义以序列化std::vector<struct B>的{​​{1}}数据成员时,它无法调用为A定义的方法,并且而是调用基本基元的序列化函数(前三个定义在代码示例的顶部)。我看不到为什么B的序列化方法(byte_contentserializedeserialize)在这种情况下没有被调用,因为它们已定义。

我怀疑我缺少关于如何选择函数模板重载的一些基本规则,但我确实不确定。任何见识将不胜感激。

编辑1

更确切地说,关键问题是B的序列化发生时,实际上会在下面调用预期的方法

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调用通用的std::vector序列化方法

T = struct B

但是当执行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);
}

我真的很困惑为什么会这样。

编辑2

添加@Evg推荐的前向声明后,该代码几乎可以按我期望的那样工作。现在唯一的问题是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); } 的{​​{1}}专长没有被调用。可以通过将上述byte_content的专业化定义替换为

来验证这一点
B

并见证了“ 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); } 专长。

1 个答案:

答案 0 :(得分:2)

注意:此答案指的是编辑之前的问题(无向前声明)。

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);

在一开始。