其他类型的va_arg返回0



我正试图使用模板可变参数的概念来编写一个通用向量类。Vector类声明的本质及其属性如下所示。

template<unsigned size, typename T>
struct Vector {
T data[size];
};

示例

在下面提供的示例中,我创建了两个向量3s,一个是类型int,另一个是float。然后,我调用一个Set函数(下一节中的实现(,传入相同的参数,但类型不同。int类型的向量的结果是正确的,但float类型的向量结果不是我所期望的。

Math::Vector<3, int> i_vec3;
i_vec3.Set(1, 0, 2);
std::cout << i_vec3 << 'n';
Math::Vector<3, float> f_vec3;
f_vec3.Set(1.0f, 0.0f, 2.0f);
std::cout << f_vec3 << 'n';

输出

1,0,2
1,0,0

预期输出

1,0,2
1,0,2

观察:当用int作为type调用va_arg(va_list ap, type)时,结果是正确的。但是在使用其他类型(如float(时,只返回0。


集合的实现

具有可变参数的成员方法。

void Set(T v, ...) {
va_list args;
va_start(args, v);
data[0] = v;
for (unsigned i = 1; i < size; ++i)
data[i] = va_arg(args, T);
va_end(args);
}

再现性示例

这是一个用于测试的问题的最小版本。

#include <iostream>
#include <cstdarg>
namespace Math {
template<unsigned size, typename T>
struct Vector {
T data[size];
void Set(T v, ...) {
va_list args;
va_start(args, v);
data[0] = v;
for (unsigned i = 1; i < size; ++i)
data[i] = va_arg(args, T);
va_end(args);
}
};
template<unsigned size, typename T>
std::ostream& operator<<(std::ostream& os, const Vector<size, T>& v) {
os << v.data[0];
for (unsigned i = 1; i < size; ++i)
os << ',' << v.data[i];
return os;
}
}
int main() {
Math::Vector<3, int> i_vec3;
i_vec3.Set(1, 0, 2);
std::cout << i_vec3 << 'n';
Math::Vector<3, float> f_vec3;
f_vec3.Set(1.0f, 0.0f, 2.0f);
std::cout << f_vec3 << 'n';
return 0;
}

备注

这是我第一次尝试各种各样的论点,所以很高兴知道我可能有什么误解导致了这个意想不到的结果。

您有未定义的行为,浮点的va_arg只能与double一起使用,而不能与float一起使用。点击此处了解更多

作为变通方法,您可以添加:

template<unsigned size, typename T>
struct Vector {
T data[size];
using Type = std::conditional_t<std::is_floating_point_v<T>,double,T>;

为了检查T是否为浮点,如果是,请使用double。呼叫是:

data[i] = va_arg(args, Type);

,...在C中很好,在C++中应该使用可变函数模板:

template<class ... Args>
void Set2(Args... args)
{
int idx = 0;
int fakeArray[] = { (data[idx++] = args,0)... };
static_cast<void>(fakeArray);
}

在参数包大小上使用static_assert的固定版本:

现场演示

由于默认参数提升,您的代码具有未定义的行为。

可变参数:默认转换:

当调用变元函数时,在从左值到右值、从数组到指针以及从函数到指针的转换之后,作为变量参数列表一部分的每个参数都会进行额外的转换,称为默认参数提升:

  • std::nullptr_t转换为void*
  • float变元转换为double,如同浮点提升一样
  • boolcharshort和无范围枚举被转换为int或更宽的整数类型,如整数提升中所示

您的编译器会警告您这一点:

警告:将经过默认参数提升的对象传递到"va_start"具有未定义的行为[-Wvarargs]

因此,代码中发生的情况是,在va_start(args, v);va_arg(args, T)中,您引用的是float,但可变部分是double

相关内容

  • 没有找到相关文章

最新更新