在 C++ 中修复类型转换警告的最佳方法

Best way to fix type conversion warnings in c++

本文关键字:警告 最佳 方法 类型转换 C++      更新时间:2023-10-16

所以,当我收到这样的警告时,我一直不确定该怎么做,所以我想得到其他专业程序员的建议。 当我收到此类警告(不是错误)时

警告 C4267:"=":从"size_t"转换为"ULONG",可能丢失 数据数量

(省略其他上下文代码)

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()返回一个size_t,而authIdent.PasswordLength是一个ULONG。 处理此类警告的最佳方法是什么? 他们希望我使用而不是 wcslen() 的 winapi 函数吗?

编辑:感谢大家的所有精彩回应!

每个编译器警告都需要根据具体情况进行处理。

您在平台上收到此警告,因为ULONG是 32 位无符号,std::size_t是 64 位无符号。

但是鉴于 (1) 你的字符串不太可能超过 ULONG 长,以及 (2) 两个无符号类型之间的转换总是明确定义的,我会做务实的事情并使用static_cast

static_cast<ULONG>(wcslen(pszPwd))

由于您可以合理地相信长度适合 ULONG,因此您可以安全地使用:

static_cast<ULONG>(wcslen(pszPwd))

否则,您可以:

  • 在转换之前检查值
  • 或使用模板自动执行此操作:https://stackoverflow.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;
}