首页 > 解决方案 > 使用 Lambda 解包可变参数时增加指针

问题描述

我有以下代码,它有一个函数VariadicWrite,这个函数接受一个指针,它可以通过增加它来修改它指向不同的内存位置,具体取决于写入它的数据量:

#include <iostream>
#include <cstring>

template<typename T>
T Read(void* &ptr) noexcept
{
    T result;
    memcpy(&result, ptr, sizeof(result));
    ptr = static_cast<T*>(ptr) + 1;
    return result;
}

template<typename T>
void Write(void* &ptr, T result) noexcept
{
    memcpy(ptr, &result, sizeof(result));
    ptr = static_cast<T*>(ptr) + 1;
}

template<typename... Args>
auto VariadicWrite(void*& ptr, Args&&... args)
{
    (Write(ptr, args), ...); //unpack Args
    return 0;
}

int main()
{
    void* ptr = malloc(1024);
    memset(ptr, 0, 1024);
    void* temp = ptr;

    VariadicWrite(ptr, 1, 2, 3);
    
    std::cout<<Read<int>(ptr)<<"\n";
    std::cout<<Read<int>(ptr)<<"\n";
    std::cout<<Read<int>(ptr)<<"\n";

    free(temp);
    return 0;
}

0, 0, 0, 0这里的问题是如果我使用代码会打印出来: void*& ptr。如果我这样做void* ptr,它会打印出来,0, 1, 2, 3ptr指针永远不会增加。

如何修改 的ptr指针VariadicWrite?我认为这void*&会奏效,但在这种情况下它不会:S

标签: c++c++17

解决方案


问题是您的VariadicWrite()调用将修改ptr为指向您写入数据的结尾。然后,您Read()无需将指针重置回起点即可调用,因此您从缓冲区的未初始化部分读取零,该部分位于已写入数据之后。

ptr = temp;在写入和读取之间插入,看看是否可以修复它。

void* ptr不起作用的原因是每次调用Write(ptr, ...)都会在函数范围内增加参数的本地副本Write()。调用后变量ptrinVariadicWrite()不会改变,Write()因此下一次调用将使用相同的值。

如果您更改为VariadicWrite(void* ptr, …)and Write(void*& ptr, …),您可能会得到您想要的行为。但我建议这是一个坏主意。

正如我们从您的示例中的错误中看到的那样,知道函数是否会修改传递引用参数至关重要,但从使用该函数的代码中并不明显。这往往会招致错误,就像您在此处创建的错误一样。一个不一致的接口,VariadicWrite()不修改它的参数而是修改Write(),只会使避免这种错误变得更加困难。

通常,最好避免非常量引用,因为它们通常会导致这样的错误。我建议返回新指针而不是修改参数。

template<typename T>
void* Write(void* ptr, const T& arg)
{ 
    return static_cast<T*>(ptr) + 1;
}

template<typename... Args>
void* WriteV(void* ptr, Args&&... args)
{
    ((ptr = Write(ptr, args)), ...);
    return ptr;
}

推荐阅读