va_arg 与其他类型 return 0
va_arg with other types return 0
我正在尝试使用 模板 和 可变参数 的概念编写通用向量 class。我的 Vector class 声明及其属性的本质如下所示。
template<unsigned size, typename T>
struct Vector {
T data[size];
};
例子
在下面提供的示例中,我创建了 2 个向量 3,一个是 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
Observation: When calling va_arg(va_list ap, type)
with int
as type
the result is correct. But upon using other types such as float
, 0 is just being returned.
集合的实现
Member method with variadic arguments.
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
。 More here
作为解决方法,您可以添加:
template<unsigned size, typename T>
struct Vector {
T data[size];
using Type = std::conditional_t<std::is_floating_point_v<T>,double,T>;
用于检查T
是否为float,是的话,使用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 的固定版本:
由于默认参数提升,您的代码有未定义的行为。
Variadic arguments: Default conversions:
When a variadic function is called, after lvalue-to-rvalue, array-to-pointer, and function-to-pointer conversions, each argument that is a part of the variable argument list undergoes additional conversions known as default argument promotions:
std::nullptr_t
is converted to void*
float
arguments are converted to double
as in floating-point promotion
bool
, char
, short
, and unscoped enumerations are converted to int or wider integer types as in integer promotion
你的编译器会警告你:
warning: passing an object that undergoes default argument promotion to 'va_start' has undefined behavior [-Wvarargs]
那么您的代码中发生的事情是,在 va_start(args, v);
和 va_arg(args, T)
中您引用了 float
,但可变参数部分是 double
.
我正在尝试使用 模板 和 可变参数 的概念编写通用向量 class。我的 Vector class 声明及其属性的本质如下所示。
template<unsigned size, typename T>
struct Vector {
T data[size];
};
例子
在下面提供的示例中,我创建了 2 个向量 3,一个是 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
Observation: When calling
va_arg(va_list ap, type)
withint
astype
the result is correct. But upon using other types such asfloat
, 0 is just being returned.
集合的实现
Member method with variadic arguments.
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
。 More here
作为解决方法,您可以添加:
template<unsigned size, typename T>
struct Vector {
T data[size];
using Type = std::conditional_t<std::is_floating_point_v<T>,double,T>;
用于检查T
是否为float,是的话,使用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 的固定版本:
由于默认参数提升,您的代码有未定义的行为。
Variadic arguments: Default conversions:
When a variadic function is called, after lvalue-to-rvalue, array-to-pointer, and function-to-pointer conversions, each argument that is a part of the variable argument list undergoes additional conversions known as default argument promotions:
std::nullptr_t
is converted tovoid*
float
arguments are converted todouble
as in floating-point promotionbool
,char
,short
, and unscoped enumerations are converted to int or wider integer types as in integer promotion
你的编译器会警告你:
warning: passing an object that undergoes default argument promotion to 'va_start' has undefined behavior [-Wvarargs]
那么您的代码中发生的事情是,在 va_start(args, v);
和 va_arg(args, T)
中您引用了 float
,但可变参数部分是 double
.