【问题标题】:Compiler Hint: "Inline function '...' has not been expanded..."编译器提示:“内联函数'...'尚未扩展...”
【发布时间】:2010-10-13 13:51:38
【问题描述】:

在一个单元中我使用函数DeleteFile,编译器输出一个提示:

“H2443 内联函数 'DeleteFile' 未展开,因为在使用列表中未指定单元 'Windows'”

Uses 中有SysUtils,它定义了DeleteFile(虽然内部调用了Windows.DeleteFile)。

这个提示是什么意思?如果我将Windows 放入Uses 子句中,它就消失了,但我想了解是什么困扰了编译器。

【问题讨论】:

    标签: delphi winapi


    【解决方案1】:

    这是一个内联限制。

    请参阅 Hallvard Vassbotn 关于Inlined Routines 的文章。

    从该网站提取:

    其余的内联限制 对于这两个平台和 最重要的是

    • 没有跨包边界的内联
    • 内联例程无法访问实现部分标识符
    • 调用站点必须有权访问内联中使用的所有标识符 例行公事

    注意 最后一点的意思是,除非调用站点单元使用例程所需的单元,否则不能内联例程。发生这种情况时,编译器会发出这样的提示

     [Pascal Hint] InlinedRoutinesU.pas(14): H2443 Inline function 
       'InlineMe' has not been expanded because unit 'RequiredUnit' 
        is not specified in USES list 
    

    要解决此问题,请将缺少的单位名称添加到调用站点的使用子句中。

    【讨论】:

      【解决方案2】:

      内联函数可以内联扩展。例如:

      function AddPlus(const A,B: Integer): Integer; inline;
      begin
        Result := A + B + 1;
      end;
      
      var
        x,y,z : Integer;
      begin
        y := 22;
        z := 11;
        x := AddPlus(y, z);
      end.
      

      被改写为:

      var
        x,y,z : Integer;
      begin
        y := 22;
        z := 11;
        x := y+z+1;
      end.
      

      这消除了函数调用的开销。

      但是为了用函数体替换调用,编译器需要更多信息,因此对单元的抱怨。​​

      请注意,并非所有内联函数都被转换。有些被视为普通函数(由编译器决定)。此外,只有在性能瓶颈非常严重时才需要内联。

      【讨论】:

        【解决方案3】:

        我也被这个提示弄糊涂了。然后我意识到问题是什么。你的代码:

        uses
           SysUtils;
        
        procedure TForm1.DoStuff;
        begin
           SysUtils.DeleteFile('foo');
        end;
        

        实际上被替换为:

        uses
           SysUtils;
        
        procedure TForm1.DoStuff;
        var
          Flags, LastError: Cardinal;
        begin
          Result := Winapi.Windows.DeleteFile(PChar(FileName));
        
          if not Result then
          begin
            LastError := GetLastError;
            Flags := GetFileAttributes(PChar(FileName));
        
            if (Flags <> INVALID_FILE_ATTRIBUTES) and (faSymLink and Flags <> 0) and
              (faDirectory and Flags <> 0) then
            begin
              Result := RemoveDirectory(PChar(FileName));
              Exit;
            end;
        
            SetLastError(LastError);
          end;
        end;
        

        如果您注意到,您的 "new" 代码取决于 WinApi.Windows 单位:

        Result := Winapi.Windows.DeleteFile(PChar(FileName));
        

        您没有包含在您的 uses 子句中。

        如果您手动内联了代码(复制和粘贴),则在您将 Windows 添加到您的 uses 之前,代码将无法编译。

        相反,编译器不会执行inline,因为:

        内联函数 'DeleteFile' 未展开,因为在使用列表中未指定单元 'Windows'"

        【讨论】:

        • "实际上被替换为" - 不像您展示的那样,因为内联过程必须进行调整以处理函数调用的参数和结果值,该函数调用在之后不再是函数调用应用内联。但是您的答案是正确的 - SysUtils.DeleteFile() 内部的代码被带入调用代码,它需要引用 Windows 单元以满足 API 调用,否则内联将无法编译。
        【解决方案4】:

        内联函数由编译器展开,避免了函数调用的开销。例如。对于平方,sqr(x) 编译为 x*x,而不是调用乘以 x 并返回结果的函数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-12-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-23
          • 2011-01-25
          相关资源
          最近更新 更多