gpt4 book ai didi

c++ - 元组的可变参数模板被反转

转载 作者:行者123 更新时间:2023-12-03 10:05:08 28 4
gpt4 key购买 nike

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

#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;
}
但是,如果我这样做:
auto args = std::tuple<Args...>();
std::apply([&arguments](auto&... args) {((args = Read<std::decay_t<decltype(args)>>(arguments)), ...);}, args);
然后 args = [a, b, c] .它的顺序正确。在后面的代码中,我使用逗号运算符读取每个值并将其分配给元组。
我试过: std::invoke(func, Read<std::decay_t<Args>>(arguments)...);这导致参数的顺序也是相反的。
所以..以下两者有什么区别:
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
为什么第一个以相反的顺序创建一个元组?
有没有比使用 std::apply 更好的方法?得到正确的顺序?

最佳答案

函数调用表达式中的参数是不确定的( [expr.call]/8 )。也就是说,在对 std::make_tuple(Read<int>(arguments), Read<int>(arguments), Read<int>(arguments)) 的调用中在您的代码中,Read<int>(arguments)调用以实现想要执行的任何顺序发生,而不是按照它们编写的顺序。这不是未定义的行为,因为调用之间存在序列点,因此您不会尝试修改 arguments多次“一次”。只是程序被标准定义为有六种可能的行为(执行调用的不同顺序),并没有指定发生哪一种(甚至在同一实现下的编译/运行/调用之间是一致的!)。请注意,涉及参数包这一事实并没有改变:包扩展仅通过“插入”扩展语法代替扩展来处理,然后根据非包规则( [temp.variadic]/8 )处理。
当您使用“固定”版本时,代码扩展为基本上包含

arg1 = Read<int>(arguments), arg2 = Read<int>(arguments), arg3 = Read<int>(arguments);
, s 现在是逗号运算符,而不是函数调用语法的一部分。逗号运算符的操作数从左到右是确定顺序的。
老实说,我不知道有什么比你做的更简单的方法来获得正确的订单。实际上,我认为我们有不幸的情况,如果函数调用的参数表达式具有必须按确定顺序排序的副作用,那么就无法直接从参数表达式初始化函数参数(没有拷贝或移动)。当然,这里不是问题。尽管如此,您的“固定”版本还是提出了不必要的要求,即所涉及的类型也是可默认构造和可分配的,这不是必需的。请注意,无论出于何种原因,如果您使用大括号(列表初始化)来调用类的构造函数(在我们的示例中为 std::tuple),那么参数表达式的计算将从左到右排序,保证 ( [dcl.init.list]/4 )。
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));
}
我不知道普通函数调用的类似物,所以我们仍然会招致移动,这很可悲(尽管我希望你写的类型没有昂贵的移动!)。
另外,我确定您已经知道这一点,但请注意,这整个业务与 void*还有 Actor 阵容和所有看起来非常不安全的东西。特别是,即使假设所有这些函数的调用者都“正确”地做它,函数本身也没有正确处理对齐(或根本......)。

关于c++ - 元组的可变参数模板被反转,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65261797/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com