C++ 如果类没有';没有任何成员变量?

C++ 如果类没有';没有任何成员变量?,c++,templates,perfect-forwarding,C++,Templates,Perfect Forwarding,我最近正在研究ENTT库的源代码,我发现了一些类似于以下代码片段的东西(注意,为了使我的问题简短,我已经大大简化了内容): //请注意,此类不包含任何成员变量 类纹理阅读器 { 公众: TextureLoader()=默认值; ~TextureLoader()=默认值; std::shared_ptr loadResource(const std::string&textureFilePath)const; }; 模板 std::共享的装载资源(Args&&…Args) { //请注意如何创建临

我最近正在研究ENTT库的源代码,我发现了一些类似于以下代码片段的东西(注意,为了使我的问题简短,我已经大大简化了内容):

//请注意,此类不包含任何成员变量
类纹理阅读器
{
公众:
TextureLoader()=默认值;
~TextureLoader()=默认值;
std::shared_ptr loadResource(const std::string&textureFilePath)const;
};
模板
std::共享的装载资源(Args&&…Args)
{
//请注意如何创建临时TResourceLoader以调用其loadResource成员函数
返回TResourceLoader{}.loadResource(std::forward(args)…);
}
int main()
{
std::string texFilePath=“tex.png”;
std::shared_ptr myTexture=loadResource(texFilePath);
返回0;
}

如您所见,
加载资源
函数模板能够加载任何资源类型(例如
纹理
着色器
模型
声音
,等等)。库的文档说明,理想情况下,装入器类不应包含任何成员变量。我想这是因为每次调用
loadResource
时,都会创建一个传递给它的loader类的临时函数来调用它的
loadResource
成员函数。这就是我的问题所在:
TResourceLoader{}.loadResource()
的成本是多少?编译器是否能够删除临时变量的创建,因为它不包含任何成员变量?有更好的方法吗?

是的,编译器将优化不包含任何数据成员的临时变量的创建。这基本上不需要codegen。您可以自己验证它,并在一个在线工具上使用各种优化级别,如。

应该不会有显著的性能影响,尽管代码会受到轻微的惩罚。为了更好地理解其含义,让我们尝试将代码分解为类似于编译器生成的代码的内容:

发件人:

返回TResourceLoader{}.loadResource(std::forward(args)…);
致:

char存储[1];//C++中的任何对象至少有1个字节,包括没有成员的类。
存储(和存储);//说明调用构造函数的伪代码
loadResource(&Storage,);//考虑到loadResource不能内联
存储。~Storage();
在上面的代码中,编译器将看到构造函数和析构函数都是默认的,而且由于类并没有成员,所以它们实际上是微不足道的,所以可以安全地忽略它们

最终需要在自动存储中分配1个字节,这在现代体系结构中通常意味着递减堆栈指针寄存器,然后递增它


这是一个难以置信的快速操作,但它仍然不是瞬间完成的。

是的,我认为无论谁投了反对票,都会有点兴奋。然而,这个问题中的代码肯定不是最小的。着色器之类的事情对这个问题来说并不重要,是吗?@SergeyA你说得对,但我觉得如果我不提供一些额外的背景,有人会问“为什么不让loadResource成员函数保持静态?”我只能通过展示如何使用该类来证明我的动机。@SergeyA有关更多信息,请参阅以下类。我认为ENTT的源代码非常棒,这就是为什么我总是试图从中学习的原因!哈哈哈,我想当谢尔盖问到“奇妙的机器”时,他指的是ENTT。反对票和随后的接近票是“有更好的方法吗?”的存在,通过问问题变得过于广泛或基于观点。虽然这可能是一个有价值的问题,但这里的许多人认为某些短语应该始终标记。编译器极有可能会优化“1字节分配”操作。@VittorioRomeo除非函数是内联的,否则不能。API需要一个对象。在这种情况下,所有操作都作为
loadResource
主体的一部分完成,并且
TResourceLoader
不会脱离函数边界。为什么编译器会生成代码来分配一个字节,然后立即丢弃它?优化不是函数体的局部优化吗?为什么需要内联?@VittorioRomeo由于
TextureLoader::loadResource
对编译器不可见,编译器需要完全遵循ABI。假设AMD64 ABI,指向类对象的指针也传递给函数。编译器需要有这个指针,为此它需要在某个地方分配对象!
return TResourceLoader{}.loadResource(std::forward<Args>(args)...));
char Storage[1]; // Any object in C++ is at least 1 byte, including classes with no members
Storage(&Storage); // Pseudo-code illustrating calling constructor
loadResource(&Storage, <args>); // considering loadResource can't be inlined
Storage.~Storage();