C++ gcc:如何正确使用“属性”(“可能”别名)以避免;解除围栏类型双关指针“;警告
我有一些代码使用类型双关来避免调用成员“object”的构造函数和析构函数,除非/直到实际需要使用该对象 它工作得很好,但是在g++4.4.3下,我得到了一个可怕的编译器警告:C++ gcc:如何正确使用“属性”(“可能”别名)以避免;解除围栏类型双关指针“;警告,c++,gcc,compiler-warnings,strict-aliasing,type-punning,C++,Gcc,Compiler Warnings,Strict Aliasing,Type Punning,我有一些代码使用类型双关来避免调用成员“object”的构造函数和析构函数,除非/直到实际需要使用该对象 它工作得很好,但是在g++4.4.3下,我得到了一个可怕的编译器警告: jaf@jeremy-desktop:~$ g++ -O3 -Wall puns.cpp puns.cpp: In instantiation of ‘Lightweight<Heavyweight>’: puns.cpp:68: instantiated from here puns.cpp:12:
jaf@jeremy-desktop:~$ g++ -O3 -Wall puns.cpp
puns.cpp: In instantiation of ‘Lightweight<Heavyweight>’:
puns.cpp:68: instantiated from here
puns.cpp:12: warning: ignoring attributes applied to ‘Heavyweight’ after definition
puns.cpp: In destructor ‘Lightweight<T>::~Lightweight() [with T = Heavyweight]’:
puns.cpp:68: instantiated from here
puns.cpp:20: warning: dereferencing type-punned pointer will break strict-aliasing rules
puns.cpp: In member function ‘void Lightweight<T>::MethodThatGetsCalledRarely() [with T = Heavyweight]’:
puns.cpp:70: instantiated from here
puns.cpp:36: warning: dereferencing type-punned pointer will break strict-aliasing rules
jaf@jeremy-桌面:~$g++-O3-Wall puns.cpp
puns.cpp:在“轻量级”的实例化中:
puns.cpp:68:从这里实例化
cpp:12:警告:忽略定义后应用于“重量级”的属性
puns.cpp:在析构函数'Lightweight::~Lightweight()[with T=heavweight]'中:
puns.cpp:68:从这里实例化
cpp:20:警告:取消引用类型punned指针将破坏严格的别名规则
puns.cpp:在成员函数“void Lightweight::methodThathGetScaledNevery()[with T=Heavweight]”中:
puns.cpp:70:从此处实例化
cpp:36:警告:取消引用类型punned指针将破坏严格的别名规则
我的代码试图使用gcc的_属性((_may_alias__))让gcc知道潜在的别名,但gcc似乎不理解我试图告诉它的内容。是我做错了什么,还是gcc 4.4.3只是在_may_alias__)属性上有一些问题
再现编译器警告的玩具代码如下:
#include <stdio.h>
#include <memory> // for placement new
#include <stdlib.h> // for rand()
/** Templated class that I want to be quick to construct and destroy.
* In particular, I don't want to have T's constructor called unless
* I actually need it, and I also don't want to use dynamic allocation.
**/
template<class T> class Lightweight
{
private:
typedef T __attribute((__may_alias__)) T_may_alias;
public:
Lightweight() : _isObjectConstructed(false) {/* empty */}
~Lightweight()
{
// call object's destructor, only if we ever constructed it
if (_isObjectConstructed) (reinterpret_cast<T_may_alias *>(_optionalObject._buf))->~T_may_alias();
}
void MethodThatGetsCalledOften()
{
// Imagine some useful code here
}
void MethodThatGetsCalledRarely()
{
if (_isObjectConstructed == false)
{
// demand-construct the heavy object, since we actually need to use it now
(void) new (reinterpret_cast<T_may_alias *>(_optionalObject._buf)) T();
_isObjectConstructed = true;
}
(reinterpret_cast<T_may_alias *>(_optionalObject._buf))->DoSomething();
}
private:
union {
char _buf[sizeof(T)];
unsigned long long _thisIsOnlyHereToForceEightByteAlignment;
} _optionalObject;
bool _isObjectConstructed;
};
static int _iterationCounter = 0;
static int _heavyCounter = 0;
/** Example of a class that takes (relatively) a lot of resources to construct or destroy. */
class Heavyweight
{
public:
Heavyweight()
{
printf("Heavyweight constructor, this is an expensive call!\n");
_heavyCounter++;
}
void DoSomething() {/* Imagine some useful code here*/}
};
static void SomeMethod()
{
_iterationCounter++;
Lightweight<Heavyweight> obj;
if ((rand()%1000) != 0) obj.MethodThatGetsCalledOften();
else obj.MethodThatGetsCalledRarely();
}
int main(int argc, char ** argv)
{
for (int i=0; i<1000; i++) SomeMethod();
printf("Heavyweight ctor was executed only %i times out of %i iterations, we avoid %.1f%% of the ctor calls!.\n", _heavyCounter, _iterationCounter, 100.0f*(1.0f-(((float)_heavyCounter)/((float)_iterationCounter))));
return 0;
}
#包括
#包括//用于新的放置
#包括//for rand()
/**我想快速构建和销毁的模板类。
*特别是,我不想调用t的构造函数,除非
*实际上我需要它,我也不想使用动态分配。
**/
模板类轻量级
{
私人:
typedef T___属性((__may_alias__))T_may_alias;
公众:
轻量级():_isObjectConstructed(false){/*empty*/}
~z~轻量级()
{
//调用对象的析构函数,前提是我们构造了它
如果(_isObjectConstructed)(重新解释投射(_optionalObject._buf))->~T_may_alias();
}
void methodThatGetScalledFactory()方法
{
//想象一下这里有一些有用的代码
}
void methodThatGetScaledNever()方法
{
如果(_isObjectConstructed==false)
{
//因为我们现在实际上需要使用它,所以我们需要构造这个重的对象
(无效)新的(重新解释铸造(_optionalObject._buf))T();
_Isobject=true;
}
(重新解释投射(_optionalObject._buf))->DoSomething();
}
私人:
联合{
char_buf[sizeof(T)];
未签名的长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-;
}_可选对象;
布尔_;
};
静态int_迭代计数器=0;
静态整数_heavyCounter=0;
/**需要(相对地)大量资源来构造或销毁的类的示例*/
级别重量级选手
{
公众:
重量级
{
printf(“重量级构造函数,这是一个昂贵的调用!\n”);
_重计数器++;
}
void DoSomething(){/*想象一下这里有一些有用的代码*/}
};
静态方法()
{
_迭代计数器++;
轻型obj;
如果((rand()%1000)!=0)obj.MethodThatGetScaledFarment();
else obj.methodthatGetScaledNever();
}
int main(int argc,字符**argv)
{
对于(int i=0;i我认为typedef
会混淆GCC。当直接应用于变量定义时,这些类型的属性似乎工作得最好
这个版本的课程适合我(GCC 4.6.0):
模板类轻量级
{
私人:
//typedef T___属性((__may_alias__))T_may_alias;
公众:
轻量级():_isObjectConstructed(false){/*empty*/}
~z~轻量级()
{
//调用对象的析构函数,前提是我们构造了它
如果(_){
T*uuu属性uuuu((uuu may_别名uuu))p
=(重新解释投射(_optionalObject._buf));
p->~T();
}
}
void methodThatGetScalledFactory()方法
{
//想象一下这里有一些有用的代码
}
void methodThatGetScaledNever()方法
{
T*uuu属性uuuu((uuu may_别名uuu))p
=(重新解释投射(_optionalObject._buf));
如果(_isObjectConstructed==false)
{
//因为我们现在实际上需要使用它,所以我们需要构造这个重的对象
(无效)新的(p)T();
_Isobject=true;
}
p->DoSomething();
}
[等]
我主张让包含类只包含一个大小足以包含成员“object”的字符数组然后使用placement new在字符数组的顶部进行初始化。这具有符合规范和交叉编译器的优点。唯一的问题是您必须知道成员对象的字符大小,这可能会给您带来麻烦
有什么原因不能让成员成为指针并使用new和delete吗?如果将\u
替换为指向对象的指针会怎么样:
class Lightweight
{
public:
Lightweight() : object(NULL) {/* empty */}
~Lightweight()
{
// call object's destructor, only if we ever constructed it
if (object) object->~T();
}
void MethodThatGetsCalledOften()
{
// Imagine some useful code here
}
void MethodThatGetsCalledRarely()
{
if (!object)
{
// demand-construct the heavy object, since we actually need to use it now
object = new (_optionalObject._buf) T();
}
object->DoSomething();
}
private:
union {
char _buf[sizeof(T)];
unsigned long long _thisIsOnlyHereToForceEightByteAlignment;
} _optionalObject;
T *object;
};
类轻量级
{
公众:
轻量级():对象(NULL){/*empty*/}
~z~轻量级()
{
//调用对象的析构函数,前提是我们构造了它
if(object)object->~T();
}
void methodThatGetScalledFactory()方法
{
//想象一下这里有一些有用的代码
}
void methodThatGetScaledNever()方法
{
如果(!对象)
{
//因为我们现在实际上需要使用它,所以我们需要构造这个重的对象
对象=新的(_optionalObject._buf)T();
}
对象->剂量测量();
}
私人:
联合{
char_buf[sizeof(T)];
未签名的长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-长-;
}_可选对象;
T*对象;
};
注意,没有GCC扩展,只有纯C++代码。
使用T*
而不是bool
甚至不会使更轻
更大!没问题。我已经编辑了我的示例,在GetScalledNever方法中分解出p
的定义。我认为这样更干净。性能是明显的原因。堆上和堆栈上的分配有一个重要的因素通用电气性能
class Lightweight
{
public:
Lightweight() : object(NULL) {/* empty */}
~Lightweight()
{
// call object's destructor, only if we ever constructed it
if (object) object->~T();
}
void MethodThatGetsCalledOften()
{
// Imagine some useful code here
}
void MethodThatGetsCalledRarely()
{
if (!object)
{
// demand-construct the heavy object, since we actually need to use it now
object = new (_optionalObject._buf) T();
}
object->DoSomething();
}
private:
union {
char _buf[sizeof(T)];
unsigned long long _thisIsOnlyHereToForceEightByteAlignment;
} _optionalObject;
T *object;
};