是否可以将变量分配给函数中的变量一次?

Is it possible to assign a variable to a variable in my function only once?

就拿这个有点办法:

int CMeetingScheduleAssistantApp::DoMessageBox(LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt)
{
    CString strContent = CString(lpszPrompt);
    CString strTitle = CString();

    if (!CTaskDialog::IsSupported())
        return CWinAppEx::DoMessageBox(lpszPrompt, nType, nIDPrompt);

    ENSURE(strTitle.LoadString(AFX_IDS_APP_TITLE));
    CTaskDialog dlgTaskMessageBox(strContent, _T(""), strTitle);

    // =================================
    // Can this be calculated just once?

    HDC screen = GetDC(nullptr);
    auto hSize = static_cast<double>(GetDeviceCaps(screen, HORZSIZE));
    auto hRes = static_cast<double>(GetDeviceCaps(screen, HORZRES));
    auto PixelsPerMM = hRes / hSize;   // pixels per millimeter
    auto MaxPixelWidth = PixelsPerMM * 150.0;
    auto PixelWidth = (hRes / 100.0) * 30.0;
    // =================================

    int iDialogUnitsWidth = MulDiv(
        min(static_cast<int>(PixelWidth), static_cast<int>(MaxPixelWidth)), 4, LOWORD(GetDialogBaseUnits()));
    dlgTaskMessageBox.SetDialogWidth(iDialogUnitsWidth);

    // Code snipped
}

是否可以调整此函数,使其只计算一次 MaxPixelWidth 值?无需向我的 class?

添加其他变量

我的 objective 允许多次调用 DoMessageBox 并且只计算一次最大宽度。

这可以通过标记函数局部值 static 轻松完成。静态变量最多初始化一次。由于需要将变量初始化为执行代码的结果,因此需要 C++ 的另一个强大功能:立即计算 lambda expressions.

// ================================= 注释之间的代码替换为以下内容即可完成所要求的内容。

static auto const MaxPixelWidth = []() {
    HDC screen = GetDC(nullptr);
    auto hSize = static_cast<double>(GetDeviceCaps(screen, HORZSIZE));
    auto hRes = static_cast<double>(GetDeviceCaps(screen, HORZRES));
    auto PixelsPerMM = hRes / hSize;   // pixels per millimeter
    ReleaseDC(screen);
    return PixelsPerMM * 150.0;
}();

这确保 MaxPixelWidth 最多初始化一次,无法从函数外部访问,并且可以 const 以防止对其进行意外更改。

Live demo 来说明这个概念。


请记住几件事,以帮助您了解后果,并就是否要使用上述内容做出明智的决定:

使用局部函数静态变量不是免费的。如果您查看 Compiler Explorer 的反汇编,您会发现两个细节:

  • 编译器分配一个隐藏标志,用于存储静态是否已初始化。
  • 隐藏标志在锁下更新,以防止并发线程多次初始化静态。

在访问静态变量之前,编译器发出代码来检查初始化标志。 Clang 和 GCC 似乎都使用双重检查锁定来避免在每次访问时都锁定(具有不小的成本)。这种情况下的 space 和时间开销可以忽略不计。我也尝试理解 MSVC 的代码,但没有多大意义。访问静态 可能 总是锁定会更昂贵。

底线:使用所谓的魔法静态 并不是严格意义上的零成本抽象。您总是需要为标志和线程安全的实现付费,即使在您的用例中两者都不是严格要求的。