繁体   English   中英

为什么我不能为构造函数(C ++)指定调用约定?

[英]Why can't I specify the calling convention for a constructor(C++)?

在Visual Studio 2013中,存在一个新的调用约定_vectorcall 它适用于可以在SSE寄存器中传递的SSE数据类型。

您可以指定成员函数的调用约定,如此。

struct Vector{//a 16 byte aligned type
_m128i _vectorcall operator *(Vector a); 
};  

这是有效的,它可以编译,并且尽管有16个对齐要求,类型也可以通过值传递。

另一方面,如果我尝试将它附加到任何构造函数(这似乎完全合乎逻辑),它就会失败。

struct Vector
 _vectorcall Vector(SomeOtherTypeWith16Alignment a);
}; 

编译器发出警告消息(我有警告错误):

警告C4166:构造函数/析构函数的非法调用约定。

强迫我将代码更改为:

struct Vector{
   Vector(SomeOtherTypeWith16Alignment a); //fails to compile
}; 

这也无法编译,因为现在SomeOtherTypeWith16Alignment不能通过值传递,因为在构造函数上未启用_vectorcall。

所以我被迫改变它。

struct Vector{
  Vector(const SomeOtherTypeWith16Alignment& a);
};

哪个编译。 但它不再使用_vectorcall,可能不会传递SSE寄存器中的数据,因为我更喜欢......

所以基本上,为什么我不能指定构造函数使用的调用约定?

这可能是Visual C ++特定的(_vectorcall当然是)。 我没有在其他编译器上试过这个 -

这不是一个答案,而是更多关于这种情况的观察集合。 这确实是一个有趣的问题,因为似乎有一些东西(我不知道是什么)使得构造函数(和析构函数)从根本上与调用约定不兼容。

首先,不要在C ++标准中寻找答案,因为标准并不关心调用约定,事实上,标准中唯一提到的就是作为一个超出范围的例子。该文件(即“实施定义”)。 因此,该标准不“禁止”为构造函数(或析构函数)指定或使用不同的调用约定的可能性。

从一些研究和测试来看,不同的主要编译器的情况如下(尽我所能收集):

  • MSVC允许您指定一个调用约定,但忽略它(带有警告)除“stdcall”以外的任何内容,它认为这意味着“thiscall”(stdcall-style thiscall),这已经是默认值了。 通过命令行选项(例如/Gz )更改默认调用约定不会影响非静态成员函数(如构造函数)。 换句话说,没有办法改变构造函数的调用约定,无论你试图指定什么,它都将被忽略。
  • ICC和GCC都忽略(带有警告)任何在构造函数上指定调用约定的尝试。
  • Clang忽略(带有警告)任何在构造函数上指定调用约定的尝试,除非它是“cdecl”,它也是默认值(cdecl-style thiscall)。

换句话说,除了默认编译器之外,所有编译器似乎都非常顽固地不允许构造函数上的任何其他类型的调用约定。 核心问题是:为什么?

以下是对此的一些想法(推测)。

首先,传统上,调用约定更多地是关于二进制兼容性而不是性能优化,即,在链接和调用来自不同语言或编译器的函数时指定调用约定(例如,传统上在C,C ++,Pascal和Fortran之间)。 因此,这可以解释为什么某些C ++功能被排除在外。 例如,如果您正在为C ++库创建一个接口(可能是从另一种语言调用它),那么您必须回退到C API,这意味着没有类,没有构造函数,没有成员函数,或者您有在具有标准ABI(例如,Itanium)的平台上公开C ++ API,该API修复了调用约定。 这使得从二进制兼容性的角度来看,为构造函数指定调用约定是完全无用的。 这可以解释为什么这被“忽略”(编译器未实现)作为低优先级功能。

这里可能涉及的另一个问题是,对于构造函数而言,如果您在Itanium ABI规范中查找示例,您可以看到构造函数调用非常特殊。 特别是,虚拟继承需要传递临时虚拟表指针。 这可以解释为什么编译器实现者为构造函数提供每个可能的调用约定的变体要困难得多。 换句话说,它不仅仅是将“this”指针添加为第一个“hidden”参数,然后应用标准C调用约定(就像使用普通的非静态成员函数一样)。 我不认为为构造函数实现那些替代调用约定是不可能的,但是编译器供应商根本没有实现(还)它将是额外的麻烦。 此外,给定对基类构造函数的调用,它也可能会产生诊断问题(验证基类构造函数调用约定是否与派生类调用约定兼容)。 所以,这可能仅仅是“太麻烦;没有做到”的情况。 事实上,标准允许它,但没有编制者这样做是一个强有力的指标,可能是这种情况。

除此之外,我无法提供任何明确的答案,说明为什么这在某种程度上是不可能实现的。 唯一可以获得明确答案的方法是,如果您找到一个直接参与实现其中一个编译器的人,并且已经考虑过尝试在构造函数上支持不同的调用约定。 请注意,这样的人甚至可能不存在(您最好的选择可能是检查LLVM / Clang开发人员社区是否有人调查过这个问题)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM