gcc:如何正确使用__attribute((__may_alias__))以避免"derefencing type-punned pointer"警告
gcc: How to use __attribute((__may_alias__)) properly to avoid "derefencing type-punned pointer" warning
我有一些代码使用类型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: 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_alies__属性上有一些问题?
玩具代码再现编译器警告如下:
#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;
}
我认为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.]
我认为让你的包含类只包含一个足够大的char数组来包含你的成员"object",然后使用placement new在char数组顶部初始化。它具有规范兼容以及交叉编译器的优点。唯一的问题是,您必须知道成员对象的字符大小,这可能会给您带来麻烦。
你不能让成员成为指针并使用new和delete是有原因的吗?
如果将_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
变得更大!
相关文章:
- 警告处理为错误这里有什么问题
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- cppcheck在const std::string[]上引发警告
- GCC对可能有效的代码抛出init list生存期警告
- 如何在BST的这个简单递归实现中消除警告
- 关于std::move的使用,是否有编译警告
- g++ 在某个类成员未初始化时不发出警告
- 警告"C++ requires a type specifier for all declaration"地图
- 警告的原因是什么:"when type is in parentheses, array cannot have dynamic size"?
- Clang 对使用的类型别名发出"unused type alias"警告
- 'Incomplete type'在VS中使用glm::vec3和ReSharper时的警告
- 海湾合作委员会 7、aligned_storage 和 "dereferencing type-punned pointer will break strict-aliasing rules"
- 处理"Thrown exception type is not nothrow copy constructible"警告
- 为什么我总是得到 - 警告:控制达到非无效函数的末尾 [-Wreturn-type]
- 海湾合作委员会警告"dereferencing type-punned pointer will break strict-aliasing rules"
- 警告:控制到达非空函数结束[- return-type]
- 在这段代码中,我有一个警告:控制达到非void函数的结束[- return-type].我怎样才能解决这个问题
- 修复警告"comparison is always false due to limited range of data type [-Wtype-limits]"
- gcc:如何正确使用__attribute((__may_alias__))以避免"derefencing type-punned pointer"警告
- 忽略 GCC "error: braces around scalar initializer for type"错误。发出警告