c++模板怪异优化
c++ template weird optimization
我像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
做任何事情,所以它忽略了错误代码。
我有两个问题:
- 我应该怎么做才能让gcc报告我的错误(而不更改我的代码),比如添加一些编译器标志
- 如果有人对此有更可靠的解释?一些官方医生会这么做
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,我认为这是合适的。
- 空基优化子对象的地址
- 关闭||运算符优化
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- 返回值优化:显式移动还是隐式
- 人脸跟踪arduino代码的优化
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 纯函数,为什么没有优化
- 为什么大多数 pair 实现默认不使用压缩(空基优化)?
- 如何以优化的方式同时迭代两个间距不相等的数组
- 小字符串优化(调试与发布模式)
- 浮点定向舍入和优化
- Visual Studio 调试优化如何工作?
- 为什么开关的优化方式与 c/c++ 中的链接不同?
- 线性优化目标函数中的绝对值
- GCC 会优化内联访问器吗?
- gcc 如何优化此循环?
- 如何防止 CUDA-GDB 中的<优化输出>值
- 为什么我的程序在 O0 和 O2 的优化级别返回不同的结果
- 这个C++编译器优化(在自身的实例上调用对象自己的构造函数)的名称是什么,它是如何工作的?
- 使用 std::p air 进行返回值优化