2016-05-14 9 views
0

IsoCpp.orgが新たな配置に関するFAQを提供しています:厳格なエイリアシング規則と配置の新

それらが提供する例は次のとおりです。

#include <new>  // Must #include this to use "placement new" 
#include "Fred.h"  // Declaration of class Fred 
void someCode() 
{ 
    char memory[sizeof(Fred)];  // Line #1 
    void* place = memory;   // Line #2 
    Fred* f = new(place) Fred(); // Line #3 (see "DANGER" below) 
    // The pointers f and place will be equal 
    // ... 
} 

は、上記のコードはplaceので、C++の厳格なエイリアシング規則に違反しないだろうし、 memoryは異なるタイプですが、同じメモリ位置を参照していますか?

(私はタイプcharのポインタは、他の型を別名設定できることを知っているが、ここではvoid*は、私が理解から許可されていないchar*を、エイリアシング持っているように見える?)

私はそのほとんどのメモリアロケータを疑います同様の方法で厳密なエイリアシング規則に違反します。新しいプレースメントを使用する場合、厳密なエイリアシング規則に準拠する適切な方法は何ですか?

はあなた

+0

標準では、厳密なエイリアシングで 'char'ポインタが実際に免除されています。コンパイラは、さまざまな構造体やその他のものを保持するために頻繁に正確なサイズのバッファを作成するために使用されるため、他のポインタが 'char *'のエイリアスになると仮定する必要があります。 'void *'を参照できないことを考えると、一般的に、コンパイラは、voidポインタを別の型にキャストして使用すると、エイリアスの問題に遭遇する可能性があると不平を言うだけです。あなたの主な問題は 'memory'と' f'エイリアシングですが、 'memory'は' char * 'です。 – RyanP

+0

@RyanP、説明のおかげで;私は厳密なエイリアシングにはまったく新しいので、実際にUBを呼び出すためには逆参照する必要があることを認識していませんでした。私は 'char *'エイリアシング免除について認識していましたが、 'char *'はエイリアス(およびderef)型の 'T'を意味しますが、' T'だけエイリアスとderef 'T *'自体が 'char *'型であれば 'char *'型ですか? – digitale

答えて

2

新しい配置を使用した場合、厳密なエイリアシング規則に準拠する適切な方法は何ですかありがとう!

正しい方法はstd::aligned_storageです。このコードサンプルでは、​​Fredのストレージの正しい配置が保証されていないため、使用しないでください。

これを行うための正しい方法は次のとおりです。

#include <new>   // For placement new 
#include <type_traits> // For std::aligned_storage 

struct Fred { 
    // ... 
}; 

void someCode() { 
    std::aligned_storage<sizeof(Fred), alignof(Fred)>::type memory; 
    // Alternatively, you can remove the "alignof(Fred)" template parameter if you 
    // are okay with the default alignment, but note that doing so may result in 
    // greater alignment than necessary and end up wasting a few bytes. 
    Fred* f = new(&memory) Fred(); 
} 

は、上記のコードは、場所とメモリが異なったタイプであるため、C++の厳格なエイリアシング規則に違反し、まだ同じメモリ位置を参照しませんか?

さて、元のコードでfplace、およびmemory間エイリアシングについてのあなたの懸念については、いかなるエイリアシング違反がないことに注意してください。厳密なエイリアシングルールは、 "dereference a pointer that aliases an incompatible type"にはできないことを意味します。 void*を逆参照することはできません(void*との間でポインタを変換することはできます)placeが厳しいエイリアス違反の原因となる危険性はありません。

+0

'std :: aligned_storage :: type'でエイリアスを許す標準の特別な例外を考えていない限り、これは質問には答えません。これはコメントに過ぎません。 – hvd

+0

@hvd:私の答えの前半は、「プレースメントの新規使用時に厳密なエイリアシング規則に準拠する正しい方法は何ですか?」をカバーすることを意図しています。新しいプレースメントを正しく使用する方法のデモでの質問。しかし、あなたは私が質問の他の部分に対処していないということは正しいです。それが私の答えの後半を追加した理由です。 – Cornstalks

+0

それは悪くはありませんが、ポインタの変換が有効かどうかは結果のポインタを安全に間接参照できるかどうかとは無関係です。任意のコードが 'memory'のバイトを直接参照すると、エイリアス違反が発生する可能性があります。しかし、そうでない場合、エイリアシングがないので、エイリアシング違反も発生しません。 – hvd

4

コードが*placeを参照していないため、問題はありません。ポインタが同じであっても、UBは発生しません - 両方とも禁止されている間接的です。

たとえば、次のように合法である:

struct A {int x;} a; 
struct B {} *pb = reinterpret_cast<B*>(&a); 
A* pa = reinterpret_cast<A*>(pb); 

*pbを参照してください、あなたは、厳密なエイリアシング規則に違反しました。もちろん、あなたの特定の例では

ことが許可されていないタイプvoidの左辺値をもたらすであろうので、あなたは*placeを書き込むことはできません。

注意点もトウモロコシの茎を作る:例は本当にmemoryが適切Fredオブジェクトのために整列されるという保証がないためstd::aligned_storageを使用する必要があります。実際には、これは重要ではありません。なぜなら、newまたはmalloc(適切に整列されたストレージを返す)のような新しい配置のメモリを割り当てたからです。

+0

また重要なのは、 'memory'のバイトを直接参照するコードがないことです。 – hvd

+0

私は*それが許されると思う - charを支配する特別な規則のために - それを避けるためには良いスタイルだろう。 –

+0

厳密なエイリアシング規則に違反するためにderefが発生しなければならないことを強調してくれてありがとう – digitale

関連する問題