【问题标题】:Templated function overload is failing to be called in recursive template call递归模板调用中无法调用模板化函数重载
【发布时间】:2019-06-21 06:04:18
【问题描述】:

我遇到了一个问题,即为某些模板类型执行适当的函数模板重载。查看我遇到的情况所需的最小示例如下所示:

#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_contentserializedeserialize 的风格通过适当的定义被调用。但是,当程序递归到这些方法的通用std::vector 定义以序列化Astd::vector&lt;struct B&gt; 数据成员时,它无法调用为B 定义的方法,而是调用基本的序列化函数原语(前三个定义在代码示例的顶部)。我不明白为什么 B 的序列化方法(byte_contentserializedeserialize)在这种情况下没有被调用,因为它们已定义。

我怀疑我错过了一些关于如何选择函数模板重载的基本规则,但我真的不确定。任何见解将不胜感激。

编辑 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 推荐的前向声明后,代码几乎可以正常工作。现在唯一的问题是没有调用Bbyte_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”消息从未显示。现在也许我只是累了,没有看到一些错误,但我不明白为什么,即使在前向声明之后,没有调用正确的 byte_content 专业化 B

【问题讨论】:

  • 将专用函数的声明放在泛型函数之前。
  • @Evg 我在看到你的简短回答后才更新了一些东西(不再存在),但让我试试你刚才说的话,因为这在我脑海中敲响了警钟。
  • 这个答案不正确,所以我删除了它。我重读了你的问题,看到了真正的问题。问题是在泛型方法内部,编译器不知道专门的方法。使用它们的前向声明。
  • @Evg 它非常接近工作,但是如果您看到我的编辑,我会发现其中一个功能专业化仍未被调用,并且在您推荐后我真的很惊讶,因为其他功能专业​​化现在正在工作。我觉得我可能只是犯了一些愚蠢的错误,但我什么都看不到。
  • 下次请不要对问题中的代码进行重大更改。对于以后会发现这个问题的人来说,真的很难理解答案是什么。

标签: c++ function templates overloading


【解决方案1】:

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

serialize(const std::vector&lt;T&gt;&amp; data...) 中,您使用了不合格的名称serialize。编译器应该决定调用哪个serialize。它将考虑函数 1) 在定义点可见和 2) 在实例化点可由 ADL 找到。两次查找都找不到serialize(const B&lt;id_type&gt;&amp;...)

一种可能的解决方案是提出声明

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

在一开始。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多