在 C++ 中修复类型转换警告的最佳方法
Best way to fix type conversion warnings in c++
所以,当我收到这样的警告时,我一直不知道该怎么办,所以我需要其他专业程序员的建议。当我收到
类型的警告(不是错误)时
warning C4267: '=': conversion from 'size_t' to 'ULONG', possible loss
of data
(省略其他上下文代码)
wchar_t pszName[CREDUI_MAX_USERNAME_LENGTH + 1] = L"user";
wchar_t pszPwd[CREDUI_MAX_PASSWORD_LENGTH + 1] = L"password";
// ..
COAUTHIDENTITY authIdent;
// ...
memset(&authIdent, 0, sizeof(COAUTHIDENTITY));
authIdent.PasswordLength = wcslen(pszPwd);
authIdent.UserLength = wcslen(pszName);
问题显然是 wcslen() returns a size_t 和 authIdent.PasswordLength 是一个 ULONG。处理此类警告的最佳方法是什么?他们希望我使用 winapi 函数来代替 wcslen() 吗?
编辑:感谢大家的精彩回复!
每个编译器警告都需要根据具体情况进行处理。
您会在您的平台上收到此警告,因为 ULONG
是 32 位无符号的,而 std::size_t
是 64 位无符号的。
但考虑到 (1) 您的字符串不太可能比 ULONG
长,并且 (2) 两个无符号类型之间的转换总是 定义明确,我会做务实的事情并使用 static_cast
:
static_cast<ULONG>(wcslen(pszPwd))
由于您可以合理地相信长度适合 ULONG,因此您可以安全地使用:
static_cast<ULONG>(wcslen(pszPwd))
否则你可以:
- 在转换前检查值
- 或使用模板自动完成:
https://whosebug.com /questions/998571/c-template-for-safe-integer-casts
字符串长度的最自然类型是 size_t
。实际上,字符串的大小将适合 ULONG
和 size_t
。尽管如此,这样的任务绝对值得警告。想象一下这个愚蠢的错误:
ULONG x = 0;
authIdent.PasswordLength = x - 1;
很容易声称这样的错误永远不会发生,但是通过提供一个不易出错的接口来防止这样的错误也很容易(这也可以防止丑陋的 memset
出现在用户代码中) :
struct my_COAUTHIDENTITY {
private:
COAUTHIDENTITY authIdent;
public:
my_COAUTHIDENTITY() {
memset(&authIdent, 0, sizeof(COAUTHIDENTITY));
}
void setPasswd(wstring passwd) {
...
authIdent.PasswordLength = static_cast<ULONG>(passwd.size());
}
};
一种方法是提供一个 safe_numeric_cast
函数,该函数在非调试版本中执行未经检查的 static_cast
,同时在调试版本中检查缩小转换。
例如
#include <type_traits>
#include <limits>
#include <cassert>
//#include <boost/stacktrace.hpp> // enable if you have boost 1.65+
#include <iostream>
extern void foo(std::size_t);
struct conversion_failure
{
void operator()() const
{
#ifdef NDEBUG
// welcome to undefined behaviour - or we could log, throw, etc.
#else
std::cerr << "conversion out of bounds" << std::endl;
// std::cerr << boost::stacktrace::stacktrace();
std::exit(100);
#endif
}
};
template<class From, class To, class Enable = void>
struct safe_cast;
template<class Both> struct safe_cast<Both, Both>
{
Both operator()(Both in) const {
return in;
}
};
template<
class Big,
class Small
>
struct safe_cast<Big, Small, std::enable_if_t<(std::numeric_limits<Big>::digits > std::numeric_limits<Small>::digits)>>
{
Small operator()(Big from) const {
using to_limits = std::numeric_limits<Small>;
assert(from >= Big(to_limits::min()) && from <= Big(to_limits::max()) );
return Small(from);
}
};
template<
class Small,
class Big
>
struct safe_cast<Small, Big, std::enable_if_t<(std::numeric_limits<Big>::digits > std::numeric_limits<Small>::digits)>>
{
Big operator()(Small from) const {
return from;
}
};
template
<
class To,
class From
>
auto safe_numeric_cast(From&& from)
-> decltype(auto)
{
auto conv = safe_cast<From, To>();
return conv(from);
}
int main()
{
int x = 10;
auto y = safe_numeric_cast<long long>(x);
std::cout << y << std::endl;
auto z = safe_numeric_cast<int>(y);
std::cout << z << std::endl;
// should abort on debug build, UB on release
auto zz = safe_numeric_cast<int>(std::numeric_limits<long long>::max());
std::cout << zz << std::endl;
}
所以,当我收到这样的警告时,我一直不知道该怎么办,所以我需要其他专业程序员的建议。当我收到
类型的警告(不是错误)时warning C4267: '=': conversion from 'size_t' to 'ULONG', possible loss of data
(省略其他上下文代码)
wchar_t pszName[CREDUI_MAX_USERNAME_LENGTH + 1] = L"user";
wchar_t pszPwd[CREDUI_MAX_PASSWORD_LENGTH + 1] = L"password";
// ..
COAUTHIDENTITY authIdent;
// ...
memset(&authIdent, 0, sizeof(COAUTHIDENTITY));
authIdent.PasswordLength = wcslen(pszPwd);
authIdent.UserLength = wcslen(pszName);
问题显然是 wcslen() returns a size_t 和 authIdent.PasswordLength 是一个 ULONG。处理此类警告的最佳方法是什么?他们希望我使用 winapi 函数来代替 wcslen() 吗?
编辑:感谢大家的精彩回复!
每个编译器警告都需要根据具体情况进行处理。
您会在您的平台上收到此警告,因为 ULONG
是 32 位无符号的,而 std::size_t
是 64 位无符号的。
但考虑到 (1) 您的字符串不太可能比 ULONG
长,并且 (2) 两个无符号类型之间的转换总是 定义明确,我会做务实的事情并使用 static_cast
:
static_cast<ULONG>(wcslen(pszPwd))
由于您可以合理地相信长度适合 ULONG,因此您可以安全地使用:
static_cast<ULONG>(wcslen(pszPwd))
否则你可以:
- 在转换前检查值
- 或使用模板自动完成: https://whosebug.com /questions/998571/c-template-for-safe-integer-casts
字符串长度的最自然类型是 size_t
。实际上,字符串的大小将适合 ULONG
和 size_t
。尽管如此,这样的任务绝对值得警告。想象一下这个愚蠢的错误:
ULONG x = 0;
authIdent.PasswordLength = x - 1;
很容易声称这样的错误永远不会发生,但是通过提供一个不易出错的接口来防止这样的错误也很容易(这也可以防止丑陋的 memset
出现在用户代码中) :
struct my_COAUTHIDENTITY {
private:
COAUTHIDENTITY authIdent;
public:
my_COAUTHIDENTITY() {
memset(&authIdent, 0, sizeof(COAUTHIDENTITY));
}
void setPasswd(wstring passwd) {
...
authIdent.PasswordLength = static_cast<ULONG>(passwd.size());
}
};
一种方法是提供一个 safe_numeric_cast
函数,该函数在非调试版本中执行未经检查的 static_cast
,同时在调试版本中检查缩小转换。
例如
#include <type_traits>
#include <limits>
#include <cassert>
//#include <boost/stacktrace.hpp> // enable if you have boost 1.65+
#include <iostream>
extern void foo(std::size_t);
struct conversion_failure
{
void operator()() const
{
#ifdef NDEBUG
// welcome to undefined behaviour - or we could log, throw, etc.
#else
std::cerr << "conversion out of bounds" << std::endl;
// std::cerr << boost::stacktrace::stacktrace();
std::exit(100);
#endif
}
};
template<class From, class To, class Enable = void>
struct safe_cast;
template<class Both> struct safe_cast<Both, Both>
{
Both operator()(Both in) const {
return in;
}
};
template<
class Big,
class Small
>
struct safe_cast<Big, Small, std::enable_if_t<(std::numeric_limits<Big>::digits > std::numeric_limits<Small>::digits)>>
{
Small operator()(Big from) const {
using to_limits = std::numeric_limits<Small>;
assert(from >= Big(to_limits::min()) && from <= Big(to_limits::max()) );
return Small(from);
}
};
template<
class Small,
class Big
>
struct safe_cast<Small, Big, std::enable_if_t<(std::numeric_limits<Big>::digits > std::numeric_limits<Small>::digits)>>
{
Big operator()(Small from) const {
return from;
}
};
template
<
class To,
class From
>
auto safe_numeric_cast(From&& from)
-> decltype(auto)
{
auto conv = safe_cast<From, To>();
return conv(from);
}
int main()
{
int x = 10;
auto y = safe_numeric_cast<long long>(x);
std::cout << y << std::endl;
auto z = safe_numeric_cast<int>(y);
std::cout << z << std::endl;
// should abort on debug build, UB on release
auto zz = safe_numeric_cast<int>(std::numeric_limits<long long>::max());
std::cout << zz << std::endl;
}