C++ 将Varadic模板转换为元组是反向的

C++ 将Varadic模板转换为元组是反向的,c++,c++17,C++,C++17,我有以下代码从指针读取值/参数并调用函数: #include <iostream> #include <functional> #include <string> #include <tuple> template<typename T> T Read(void*& ptr) { T result = *static_cast<T*>(ptr); ptr = static_cast<T*>

我有以下代码从指针读取值/参数并调用函数:

#include <iostream>
#include <functional>
#include <string>
#include <tuple>

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

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

template<typename R, typename... Args>
R Call(void* arguments, R (*func)(Args...))
{
    //args = [c, b, a] somehow..?
    auto args = std::make_tuple(Read<std::decay_t<Args>>(arguments)...);
    return std::apply(func, args);
}


void func_one(int a, int b, int c)
{
    std::cout<<"a: "<<a<<" b: "<<b<<" c: "<<c<<"\n";
}


int main()
{
    int a = 1024;
    int b = 2048;
    int c = 3072;

    int* args = new int[3];
    void* temp = args;
    
    Write(temp, a);
    Write(temp, b);
    Write(temp, c);
    
    Call(args, func_one);
    delete[] args;
    
    return 0;
}
为什么第一个以相反的顺序创建元组?
有没有更好的方法可以使用
std::apply
获得正确的顺序?

函数调用表达式中的参数顺序不确定()。也就是说,在代码中对
std::make_tuple(Read(arguments)、Read(arguments)、Read(arguments)、Read(arguments))的调用中,
Read(arguments)
调用按照实现想要的顺序进行,而不是按照编写的顺序。这不是未定义的行为,因为调用之间存在序列点,因此您不会试图“一次”多次修改
参数。只是标准将程序定义为具有六种可能的行为(执行调用的不同顺序),而它没有指定发生哪种行为(甚至没有指定在同一实现下编译/运行/调用之间的行为是一致的!)。请注意,有一个涉及参数pack的事实并没有改变任何东西:包扩展只需“插入”扩展语法来代替扩展,然后根据非包规则()进行处理

当您使用“固定”版本时,代码将扩展为基本包含

arg1 = Read<int>(arguments), arg2 = Read<int>(arguments), arg3 = Read<int>(arguments);
我不知道普通函数调用的类似情况,所以我们仍然会发生移动,这是令人悲哀的(尽管我希望您不是在用昂贵的移动编写类型!)


另外,我相信你已经知道这一点,但请注意,这整个业务与
void*
和演员阵容以及所有看起来非常不安全的事情。特别是,即使假设所有这些函数的调用者都做得“正确”,函数本身也没有正确地处理对齐(或者根本没有…。

你是说这是你文章中的第一段代码摘录以错误的顺序输出吗?gcc没有,但clang以正确的顺序输出make_元组版本。。。这会是一个排序问题吗?我也在使用Clang,它的顺序对我来说是正确的。@Brandon:正如你可能已经知道的,参数的求值顺序是未指定的。我只是环顾了一下,没有发现这是否/如何与参数包扩展交互,所以我将把它留给其他人。然而,有人似乎(可能)有类似的问题。在任何情况下,顺序在其他上下文中都是有保证的(比如在列表初始化中)。@scg Yes。第一个例外输出了错误的顺序:D我知道参数顺序,但在为变量模板编写代码时没有想到。我想它们只是按照
编写和排序的顺序来表示“下一个”参数集。我想我大错特错了。
Read<Args>(arguments)...  //tuple is reversed - a: 3072 b: 2048 c: 1024
//and
(Read<Args>(arguments)), ...)  //tuple is in the right order - a: 1024 b: 2048 c: 3072
arg1 = Read<int>(arguments), arg2 = Read<int>(arguments), arg3 = Read<int>(arguments);
template<typename R, typename... Args>
R Call(void *arguments, R (*func)(Args...)) {
    std::tuple<std::decay_t<Args>...> args{Read<std::decay_t<Args>>(arguments)...};
    return std::apply(func, std::move(args));
}