用函数替换宏会导致 "signed/unsigned mismatch" 警告
replacing macro with a function causes "signed/unsigned mismatch" warning
对于这个片段
const std::vector<int> v;
if (v.size() != 1) {} // could call operator!=()
即使在高警告级别(所有警告在 Visual Studio 2022 中启用),代码也能完全符合要求。但是,如果我将参数传递给函数
const auto f = [](auto&& lhs, auto&& rhs) { if (lhs != rhs) {}};
f(v.size(), 1);
编译器生成 '!=': signed/unsigned 不匹配 警告。
如何使函数的行为与“内联”代码相同,即没有警告?
请记住,“真实代码”类似于
#define f(lhs, rhs) if (lhs != rhs) {}
我想“执行 'right thing'”并用函数替换宏
temmplate<typename TLhs, typename TRhs>
inline void f(TLhs&& lhs, TRhs&& rhs)
{
if (lhs != rhs) {}
}
在直接比较中,编译器知道比较双方的类型,因此可以确保 1
是正确大小的无符号类型。
当您调用 lambda 时,rhs
的类型将被推断为 int
,因为那才是 1
的真正含义。由于 lhs
将是无符号类型,因此当您将无符号大小与(有符号)int
值 1
.
进行比较时,您会在比较中收到警告
编译器必须推导参数的任何类型的可调用对象都会遇到同样的问题,例如函数模板:
template<typename T, typename U>
void f(T const& lhs, U const& rhs);
对于函数模板,解决方案很简单:对两个参数使用相同的单一类型:
template<typename T>
void f(T const& lhs, T const& rhs);
你的第一个例子是一个具体案例。因此,即使您将有符号 int
与无符号 std::vector::size_type
进行比较,编译器也会告诉您没有问题。它会将 1 隐式转换为无符号类型,表示 1 没有问题。
在第二个例子中,你有一般情况。在为 (size_type
, int
) 情况实例化 lambda 函数时,必须为任何一组值做好准备。它不能假定 int
的值可以在不改变值的情况下转换为 size_type
。 (理论上,它可以内联 lambda,然后它会在这个特定的调用中看到特定的值,但显然它不会那样做。)
解决方案是使两个参数相同(或至少兼容)。
f(v.size(), 1u);
ETA: 如果你想做一个专门比较尺寸的功能,你可以在界面中使用特定的类型。
// Assuming `std::vector::size_type` is the same as `std::size_t`...
const auto f = [](std::size_t lhs, std::size_t rhs) { if (lhs != rhs) {}};
然后,当你调用f(v.size(), 1)
时,编译器会将int
转换为size_t
,从而避免比较错误。
如果你想要其他情况的通用性,你可以使用一个函数模板,然后通过明确地对 int
到尺寸类型。
通过调用 std::cmp_not_equal
将问题移出您的代码
const auto f = [](auto&& lhs, auto&& rhs) {
if (std::cmp_not_equal(lhs, rhs)) { blah blah }
};
在 C++20 中添加的这一系列函数是 defined in a way that properly compares integer arguments even in the presence of a signed/unsigned mismatch。
现在标准库作者负责编写“所有那些模板垃圾”,您需要做的只是 #include <utility>
对于断言的特定情况,您无论如何都需要一个宏来捕获信息。
#define ASSERT_EQ(actual, expected) do { \
if (lhs == rhs) break; \
log_assertion_failure(__FILE__, __LINE__, #actual, actual, expected); \
} while(0)
不确定它是否完全适用于您的情况,但您可以“强制” 一种操作数类型:
temmplate <typename T>
void f(const T& lhs, const std::struct_identity_t<T>& rhs)
{
if (lhs != rhs) {/*..*/}
}
std::type_identity 是 C++20,但可以为以前的标准实现。
对于这个片段
const std::vector<int> v;
if (v.size() != 1) {} // could call operator!=()
即使在高警告级别(所有警告在 Visual Studio 2022 中启用),代码也能完全符合要求。但是,如果我将参数传递给函数
const auto f = [](auto&& lhs, auto&& rhs) { if (lhs != rhs) {}};
f(v.size(), 1);
编译器生成 '!=': signed/unsigned 不匹配 警告。
如何使函数的行为与“内联”代码相同,即没有警告?
请记住,“真实代码”类似于
#define f(lhs, rhs) if (lhs != rhs) {}
我想“执行 'right thing'”并用函数替换宏
temmplate<typename TLhs, typename TRhs>
inline void f(TLhs&& lhs, TRhs&& rhs)
{
if (lhs != rhs) {}
}
在直接比较中,编译器知道比较双方的类型,因此可以确保 1
是正确大小的无符号类型。
当您调用 lambda 时,rhs
的类型将被推断为 int
,因为那才是 1
的真正含义。由于 lhs
将是无符号类型,因此当您将无符号大小与(有符号)int
值 1
.
编译器必须推导参数的任何类型的可调用对象都会遇到同样的问题,例如函数模板:
template<typename T, typename U>
void f(T const& lhs, U const& rhs);
对于函数模板,解决方案很简单:对两个参数使用相同的单一类型:
template<typename T>
void f(T const& lhs, T const& rhs);
你的第一个例子是一个具体案例。因此,即使您将有符号 int
与无符号 std::vector::size_type
进行比较,编译器也会告诉您没有问题。它会将 1 隐式转换为无符号类型,表示 1 没有问题。
在第二个例子中,你有一般情况。在为 (size_type
, int
) 情况实例化 lambda 函数时,必须为任何一组值做好准备。它不能假定 int
的值可以在不改变值的情况下转换为 size_type
。 (理论上,它可以内联 lambda,然后它会在这个特定的调用中看到特定的值,但显然它不会那样做。)
解决方案是使两个参数相同(或至少兼容)。
f(v.size(), 1u);
ETA: 如果你想做一个专门比较尺寸的功能,你可以在界面中使用特定的类型。
// Assuming `std::vector::size_type` is the same as `std::size_t`...
const auto f = [](std::size_t lhs, std::size_t rhs) { if (lhs != rhs) {}};
然后,当你调用f(v.size(), 1)
时,编译器会将int
转换为size_t
,从而避免比较错误。
如果你想要其他情况的通用性,你可以使用一个函数模板,然后通过明确地对 int
到尺寸类型。
通过调用 std::cmp_not_equal
const auto f = [](auto&& lhs, auto&& rhs) {
if (std::cmp_not_equal(lhs, rhs)) { blah blah }
};
在 C++20 中添加的这一系列函数是 defined in a way that properly compares integer arguments even in the presence of a signed/unsigned mismatch。
现在标准库作者负责编写“所有那些模板垃圾”,您需要做的只是 #include <utility>
对于断言的特定情况,您无论如何都需要一个宏来捕获信息。
#define ASSERT_EQ(actual, expected) do { \
if (lhs == rhs) break; \
log_assertion_failure(__FILE__, __LINE__, #actual, actual, expected); \
} while(0)
不确定它是否完全适用于您的情况,但您可以“强制” 一种操作数类型:
temmplate <typename T>
void f(const T& lhs, const std::struct_identity_t<T>& rhs)
{
if (lhs != rhs) {/*..*/}
}
std::type_identity 是 C++20,但可以为以前的标准实现。