如何使用模板类修复警告C4793?

sti*_*ijn 2 c++ c++-cli visual-studio-2010

cl /c /clr /W4编译器编译下面的代码时说

warning C4793: 'Interface::'vcall'{0}'' : function compiled as native

所以pragma似乎没有任何影响..有没有办法解决这个问题?或者这是一个错误(该编译指示是否适用于非模板类)?可以安全地禁用此警告吗?

#pragma unmanaged

struct Interface
{
  virtual void Foo() = 0;
};

template< class T >
struct UsesFunPtr
{
  UsesFunPtr()
  {
    &T::Foo;
  }
};

void DoIt()
{
  UsesFunPtr< Interface > a;
}

#pragma managed
Run Code Online (Sandbox Code Playgroud)

更新:如果我删除最后一行警告消失 - 所以按照ComicSansMs的回答:究竟是at the time of definition for the template什么时候?任何人都可以解释为什么最后一行,之后没有代码,仍然影响之前的代码?

std*_*ave 7

大修,因为(在评论中)海报解释了为什么&T:Foo; 不是问题.

这个警告的起源有些愚蠢.

经过调查,我们从微软发现以下内容,ComicSansMS也发布了以下内容:

实例化模板函数时,模板定义时的编译指示状态确定是管理还是非管理.

这是一个函数模板,而不是您正在使用的类模板.

实际上,这种功能模板实例是以谨慎的方式相关的.但是这种警告行为的特殊方式与编译过程有关.

当您使用#pragma managed结束文件时,编译器实际上正在生成用于调用和使用UsesFunPtr ctor的托管代码.它会发出警告,其中包含一些非托管代码.这是对原因的深入分析.

Thunk基本上是围绕某些vtable调用的包装函数; 维基百科有一篇关于这个主题的文章.你生成thunk的原因是因为你正在获取一个函数的地址(&T :: Foo).

如果文件的最后一行是这样的:

#pragma unmanaged 
Run Code Online (Sandbox Code Playgroud)

它会停止抱怨,即使你在该对象中有混合的托管和非托管代码.这是因为前端和后端之间存在"混淆"的问题:它将使用上一个#pragma命令编译我上面提到的代码.

如果您注意到,此警告不是编译时警告.它在编译之后,当它显示"生成代码......"时如果您修改DoIt()函数,如下所示:

void DoIt()
{
  long a;
  long long b;
  b = 4;
  a = b;
  UsesFunPtr< Interface > d;
}
Run Code Online (Sandbox Code Playgroud)

在"编译x.cpp"期间,您将收到有关截断的警告,然后它将转到代码生成阶段(生成代码...),它将在此处提供此问题的警告.

编译器是解析等的前端,并创建一种中间二进制格式,类似于Java字节码.代码生成器是后端,它通过此字节码(在本例中为Windows x86)在目标平台上创建实际输出.

在后端获取代码之前不会进行优化.thunk是一种优化形式,因此,它是代码生成器(采用半编译的IL代码)来发出警告,而不是编译器.这不是一种可以以我所知的任何方式关闭的优化,因为它是一种标准做法.

然而,编译器实例化该模板; 后端只看到一个完整的类,并被告知要理解它.

编译托管C++/CLI时有所不同.有时编译器可以使用所谓的链接时代码生成.发生这种情况时,链接器就是调用后端的链接器; 当它不可能时(由于各种原因),它会经历一般的编译过程(前端 - >后端 - >链接器).

#pragma按接收顺序传递给IL.这似乎表明,在#pragma managed发生之后,UsesFunPtr的函数支持代码实际上"附加到了末尾".因此,即使您的代码位于非托管空间中,代码生成器也会看到:

  1. 非托管
  2. 对象定义,使用
  3. 管理
  4. 您没有编写的类的附加支持代码,无法看到与其他目标文件的接口有关:创建和复制vtable等等.

如果您认为UsesFunPtr ctor甚至类是完全托管或非托管代码,则生成器无法区分,因为它获得的IL并不真正连接这两者.它看到一个函数创建它是非托管的(通过编译指示),并支持在其上工作并在托管空间中生成thunk的函数(通过pragma).它无法分辨连接.既然你把#pragma放在那里,它就会继续它看到的最后一个#pragma.生成的支持代码受到管理.

您会注意到,如果您将#pragma放在最后,即使存在混合模板的托管代码,您也不会收到该警告.这是因为它将该代码编译为非托管,因此thunk不是问题.

怎么总结这一切?最后管理的#pragma导致前端和后端之间的沟通错误(或者是错误的假设?),后端会抱怨它.

嘿,这是一个有趣的!