我有一些使用类型惩罚的代码,以避免必须调用成员“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: 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
我的代码尝试使用gcc的__attribute((__ 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;
}
答案 0 :(得分:5)
我认为typedef
让GCC感到困惑。当直接应用于变量定义时,这些类型的属性似乎效果最好。
这个版本的课适合我(GCC 4.6.0):
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) {
T * __attribute__((__may_alias__)) p
= (reinterpret_cast<T *>(_optionalObject._buf));
p->~T();
}
}
void MethodThatGetsCalledOften()
{
// Imagine some useful code here
}
void MethodThatGetsCalledRarely()
{
T * __attribute__((__may_alias__)) p
= (reinterpret_cast<T *>(_optionalObject._buf));
if (_isObjectConstructed == false)
{
// demand-construct the heavy object, since we actually need to use it now
(void) new (p) T();
_isObjectConstructed = true;
}
p->DoSomething();
}
[etc.]
答案 1 :(得分:2)
我认为让你的包含类只包含一个足够大的char数组来包含你的成员“object”,然后使用placement new来初始化char数组。这具有符合规范和交叉编译器的优点。唯一的问题是你必须知道你的成员对象的字符大小,这可能会让你陷入困境。
有没有理由你不能让成员成为指针并使用new和delete?
答案 2 :(得分:2)
如果用指向对象的指针替换_isObjectConstructed
该怎么办:
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; };
注意,没有GCC扩展,只有纯C ++代码。
使用T*
代替bool
甚至不会使Lightweight
更大!