c++模板怪异优化

c++ template weird optimization

本文关键字:优化 c++      更新时间:2023-10-16

我像boost一样编写了一个单例模板类:

template <typename _T>
class Singleton
{ 
public :
static _T* Instance()
{
static _T obj;
return &obj;
}
protected :
Singleton() {}
private :
struct ObjectCreator
{
ObjectCreator()
{
Singleton<_T>::instance();
}
};
static ObjectCreator object_creator;
};
template <typename _T>
typename Singleton<_T>::ObjectCreator Singleton<_T>::object_creator;

我写了主要功能来测试它

#include "Singleton.h"
class A : public Singleton <A>
{
public:
int a;
};

int main()
{
A::Instance()->a = 2;
}

我知道我在ObjectCreator的构造函数中打错了Instance,奇怪的是我可以通过gcc-4.4.7正确编译它,然后我使用了clang-6.0,它打错了我。

我想gcc可以做一些优化,因为我没有对ObjectCreator做任何事情,所以它忽略了错误代码。

我有两个问题:

  1. 我应该怎么做才能让gcc报告我的错误(而不更改我的代码),比如添加一些编译器标志
  2. 如果有人对此有更可靠的解释?一些官方医生会这么做

Ps:我知道boost会在ObjectCreate中添加一个do_nothing函数,并从Singleton<_T>:: Instance()调用它来避免这种优化

  • 我应该怎么做才能让gcc报告该错误(而不更改我的代码),比如添加一些编译器标志

您可以添加一个显式实例化template class Singleton<float>;(我只是随机选择了float作为类型,但您可以选择任何更合适的类型),以强制GCC检查语法。看见https://gcc.godbolt.org/z/ii43qX例如。

如果您只是想进行检查,还可以通过向项目中添加另一个cpp文件,将此显式实例化放到一个单独的编译单元中。

然而,进行显式实例化比隐式实例化更强,因为所有成员和方法都将被实例化。这种行为可能是不可取的(参见标准库中的示例)。

  • 如果有人对此有更可靠的解释?一些官方医生会这么做

静态成员在以需要其定义的方式使用之前不会隐式初始化(这与显式实例化非常不同)。

@StoryTeller在标准中找到了正确的段落

14.7.1隐式实例化[temp.inst]

类模板专门化的隐式实例化导致类成员函数、成员类、静态数据成员和成员模板的声明的隐式例示,而不是定义或默认参数的隐式例证;并导致成员匿名联合定义的隐式实例化。除非类模板或成员模板的成员已显式实例化或显式专门化,否则当在需要成员定义存在的上下文中引用专门化时,该成员的专门化将隐式实例化;特别地,静态数据成员的初始化(以及任何相关联的副作用)不会发生,除非静态数据成员本身以需要静态数据成员定义存在的方式使用。

编辑您应该接受@StoryTeller的回答,因为他首先正确地解释了您问题的两个方面。

如果我正确阅读了标准,我不认为您的代码格式不正确(除了使用_T标识符)。Clang多跑了一英里真是太棒了,但GCC接受这一点并没有错。

原因是您的程序只包含模板的隐式实例化。根据N19051(强调矿):

14.7.1隐式实例化[temp.inst]

1 。。。类的隐式实例化模板专门化导致的声明,而不是定义的声明或默认参数的声明类成员函数、成员类static数据成员和成员模板;并导致定义的隐式实例化成员匿名工会。除非类模板的成员或成员模板已显式实例化或显式专业化,成员的专业化是隐含的在上下文中引用专门化时实例化要求成员定义存在特别是静态数据的初始化(以及任何相关的副作用)除非静态数据成员本身在一种要求静态数据成员的定义存在的方式

Nothing使用object_creator的方式要求其定义存在。因此,只有声明才会被检查。此外,只需要实例化class ObjectCreator的声明本身,而不需要实例化其定义(或其构造函数的定义)。这与可以定义前向声明类类型的外部变量的原因相同:

extern class C c;

上面的段落(事实上没有使用object_creator)只要求实例化类型名和对象名,以产生类似于上面外部声明的效果。

因此,GCC从不需要验证instance是否有效。我想说,考虑到上面的段落,即使你没有打字错误,object_creator也很可能没有做到你认为的那样。如果您的代码有效,那只是因为函数local-staticobj在第一次使用时被初始化(使ObjectCreator成为冗余)。

至于为什么添加一个显式实例化(如@pi建议的)会立即导致错误。我们可以在这里看到:

14.7.2显式实例化〔temp.显式〕

7类模板的显式实例化专门化还显式实例化其每个成员(而不是包括从基类继承的成员),其定义为在实例化时可见,并且以前没有显式专门用于包含显式实例化。

当我们这样做时,我们递归地强制所有东西都被实例化,结果是被检查。


1-这是2005年的草案。非常接近C++03,因此,考虑到您使用GCC 4.4.7,我认为这是合适的。