GCC:__attribute((__ may_alias__))を適切に使用して、「タイプに渡るポインター」警告を避けるために適切に
-
26-10-2019 - |
質問
オブジェクトを使用する必要がある場合を除き、メンバー「オブジェクト」のコンストラクターとデストラクタを呼び出す必要がないように、タイプパンを使用するコードがいくつかあります。
正常に動作しますが、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;
}
解決
私は思います 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配列を含めるだけで、char配列の上に初期化するために新しい配置を使用することを主張します。これには、仕様に準拠していると同時に、クロスコンパイラーがあります。唯一の問題は、メンバーオブジェクトのcharのサイズを知る必要があることです。
メンバーにポインターになり、新しいものを使用して削除することができない理由はありますか?
交換した場合はどうなりますか _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*
aの代わりに bool
作らない Lightweight
もっと大きい!