属性
AttributeSpecifier:
Attribute :
Attribute DeclarationBlock
Attribute:
LinkageAttribute
AlignAttribute
Pragma
deprecated
ProtectionAttribute
static
extern
final
synchronized
override
abstract
const
auto
scope
__gshared
shared
immutable
inout
@disable
DeclarationBlock:
DeclDef
{ DeclDefsopt }
属性とは、一つ、もしくは複数の宣言を修飾するものです。 一般的な構文は:
attribute declaration; // かかっている宣言に影響
attribute: // 現在のスコープが終わるまでの
// 全ての宣言に影響
declaration;
declaration;
...
attribute { // ブロック内の全ての宣言に影響
declaration;
declaration;
...
}
リンケージ属性
LinkageAttribute:
extern ( LinkageType )
LinkageType:
C
C++
D
Windows
Pascal
System
C やシステムのAPI関数との互換性は重要ですから、 Dはそれらを簡単に呼び出す手段を提供しています。 LinkageType は大文字小文字を区別し、 実装によって拡張されることを想定しています (予約語ではありません)。 C と D は必須ですが、 その他は実装によります。 C++ は将来のために予約されています。 System は Windows 環境では Windows と同じ意味で、 それ以外の環境では C と同じ意味になります。 実装ノート: Win32向けのコンパイラは Windows と Pascal をサポートすべきです。
C の関数呼び出し規約を指定するには:
extern (C):
int foo(); // foo() は C の呼び出し規約で呼ばれる
Dの呼び出し規約ならば:
extern (D):
あるいは:
extern:
Windows API の呼び出し規約は:
extern (Windows):
void *VirtualAlloc(
void *lpAddress,
uint dwSize,
uint flAllocationType,
uint flProtect
);
アラインメント属性
AlignAttribute:
align
align ( IntegerLiteral )
以下のアラインメントの設定に用います:
- 変数
- 構造体フィールド
- 共用体フィールド
- クラスフィールド
- 構造体型、共用体型、クラス型
align と単独で書くと、 アライメントをデフォルトに戻します。 つまり、その環境のCコンパイラでのデフォルト値へ設定されます。
struct S {
align:
byte a; // オフセット 0 に配置
int b; // オフセット 4 に配置
long c; // オフセット 8 に配置
}
auto sz = S.sizeof; // 16
IntegerLiteral を指定した時は、その非デフォルト値を指定されたときの環境の C コンパイラの挙動と同じ動作をします。2 の冪乗を指定する必要があります。
1を指定すると整列は行われず、 全てのメンバがきっちり詰め込まれます。
struct S {
align (1):
byte a; // オフセット 0 に配置
int b; // オフセット 1 に配置
long c; // オフセット 5 に配置
}
auto sz = S.sizeof; // 16
フィールドへのアラインメント設定は、 そのフィールドを持つ構造体やクラス自身のアラインメントには影響しません。 それらは、その外側で設定されたアラインメントに影響されます。
align (2) struct S {
align (1):
byte a; // オフセット 0 に配置
int b; // オフセット 1 に配置
long c; // オフセット 5 に配置
}
auto sz = S.sizeof; // 14
フィールドのアラインメントを指定すると、フィールド自身のサイズに関係なく、 その 2 の冪にオフセットが揃うようになります。
struct S {
align (4):
byte a; // オフセット 0 に配置
byte b; // オフセット 4 に配置
short c; // オフセット 8 に配置
}
auto sz = S.sizeof; // 12
NewExpression で割り付けたメモリへの参照やポインタは、 size_t の倍数以外のバイト境界に揃えるのは避けてください。 ガベージコレクタは、GCの割り当てたオブジェクトへの参照やポインタは size_t バイト境界に存在すると仮定して動作しています。 そうなっていない場合、動作は未定義です。
AlignAttribute は、struct、union、class、 あるいは関数スコープに入るときにデフォルトに戻され、 スコープを抜けると元に戻ります。 基底クラスからアラインメントが継承されることもありません。
deprecated 属性
ライブラリには、 後方互換性のために非推奨の機能を残す必要が しばしば生じます。このような機能の制限には 'deprecated' と印をつけておきます。すると、 コンパイラスイッチの設定によって、 非推奨の機能を使うコードをエラーにすることが可能になります:
deprecated
{
void oldFoo();
}
実装ノート: コンパイラは、 非推奨機能のコンパイルの際に警告するかしないかを指定するスイッチを用意しておくべきです。
アクセス保護属性
ProtectionAttribute:
private
package
protected
public
export
アクセス保護属性は、 private, package, protected, public, export, のいずれか一つです。
private は、同じクラスのメンバのみが参照できる、 あるいは同じモジュールのクラス/関数のみが参照できることを示します。 private メンバをオーバーライドすることはできません。 モジュールのメンバを private と宣言するのは、 Cでの static 宣言と同等です。
package は、モジュールは違っても同じパッケージに属するコードからの アクセスは許可するようにprivateを拡張したものです。 これは、モジュールがネストしたパッケージの中にある場合は、 最も内側のパッケージについてのみ適用されます。
protected は、同じクラスかその派生クラスのメンバ、 または同じモジュールに属するコード からのみからのみ参照できることを示します。 派生クラスのメンバ関数から protected なインスタンスメンバへアクセスする場合、 そのメンバ関数の 'this' オブジェクトに属するメンバについてのみ、アクセスが可能です。 モジュールのメンバを protected とするコードは不正です。
public は、同じ実行ファイル中の全てのコードからアクセス可能なことを示します。
export は、実行ファイルの外からもアクセスできることを示します。 DLLから関数定義をエクスポートする、 というのと似ています。
保護属性は名前の探索の際には考慮されません。 具体的には、同名の2つのシンボルがスコープ内にあって、 どちらも修飾がされずに使われていれば、 保護属性によるとどちらかがアクセス不可能な場合であっても、曖昧でエラーとなります。 例:
module A;
private class Foo {}
module B;
public class Foo {}
import A;
import B;
Foo f1; // エラー、A.Foo または B.Foo
B.Foo f2; // ok
const 属性
const は、コンパイル時に評価できる定数につける属性です。 例えば:
const int foo = 7;
const {
double bar = foo + 6;
}
immutable 属性
__gshared 属性
デフォルトでは、immutable はでないグローバル宣言はスレッドローカル記憶域に配置されます。 ただし、__gshared 属性が付いているときは全スレッド間で共有されるようになります。
int foo; // それぞれのスレッド毎に独自の変数領域 foo を持つ。
__gshared int bar; // bar は全スレッドで共有される。
__gshared はメンバ変数やローカル変数にも適用可能です。 これらの場合は __gshared は static と基本的には同じ意味になり、 ただし、 スレッドローカルではなく全スレッドで共有される変数となります。
class Foo {
__gshared int bar;
}
int foo() {
__gshared int bar = 0;
return bar++; // スレッドセーフでない
}
shared と違い、__gshared スレッド間競合や動機の問題に関する安全を何も提供しません。 __gshared とマークされた変数に正しく同期アクセスするのは、 プログラマの責任です。
__gshared はセーフモードでは使用できません。
shared 属性
inout 属性
@disable 属性
@disable とマークされた宣言への参照は、 コンパイルエラーとなります。 これによって、ある種の操作やオーバーロードを、 実行時エラーにするのではなくコンパイルエラーにできます。
struct T {
@disable this(this) { } // この関数をdisableすることでTがコピー不可能となる
}
struct S {
T t; // コピー不可能メンバがあるとS全体もコピー不可能
}
@disable void foo() { }
void main() {
S s;
S t = s; // エラー、S はコピー不可能
foo(); // エラー、fooはdiableされている
}
override 属性
override は、仮想関数に適用する属性です。 これは、 そのメンバ関数は基底クラスの同名同引数の関数をオーバーライドしているのだ、 という宣言になります。override属性は、基底クラスの仮想関数の引数を変更して、 全ての派生クラスの実装を変更しなくてはならない、 という状況などに便利です。
class Foo {
int bar();
int abc(int x);
}
class Foo2 : Foo {
override {
int bar(char c); // エラー。 Foo には bar(char) という関数はない
int abc(int x); // ok
}
}
static 属性
static 属性は関数もしくはデータに適用されます。 これは、その宣言が特定のインスタンスに関するものではなく、 ある型に関するものである、という意味になります。 言い方を変えると、this を参照しない、ということです。 static がその他の宣言についた場合は無視されます。
class Foo {
static int bar() { return 6; }
int foobar() { return 7; }
}
...
Foo f = new Foo;
Foo.bar(); // 6を返す
Foo.foobar(); // エラー。Fooのインスタンスが無い。
f.bar(); // 6を返す
f.foobar(); // 7を返す
仮想関数はstaticにはできません。
staticデータは、インスタンス毎に一個ずつではなく、 プログラム中に唯一つ保持されます。
Dのstatic属性は、Cのような"ファイルローカル"という別の意味は持ちません。 この用途には、private 属性が使用されます。 例えば:
module foo;
int x = 3; // x はグローバル
private int y = 4; // y はモジュールfoo内ローカル
auto 属性
auto 属性は、他の属性がなく、 型推論を行いたいときに使用します。
auto i = 6.8; // i を double 型として宣言
scope 属性
scope 属性は、ローカル変数とクラスの宣言に使用できます。 クラス宣言では、 scope 属性をつけたクラスは、 scope クラス となります。 ローカル変数宣言の scope 属性は、RAII (Resource Acquisition is Initialization) を実装するために活用できます。 すなわち、変数がスコープを外れると、オブジェクトのデストラクタが 自動的に呼び出されます。例え例外によってスコープから出るときであっても、 やはりデストラクタが呼ばれます。要するに、scope によって、 何らかの後処理を確実に実行することが保証できます。
二つ以上の scope 変数が同じ箇所でスコープを抜ける場合、 デストラクタは、 変数が構築されたのと逆順で実行されます。
scope は、グローバルデータや静的データ、関数の ref/out パラメータには適用できません。scope の配列、関数からの scope 返値も禁止されています。 scope への初期化以外の代入も禁じられています。 Rationale: これらの制限は、 もっともな理由が見つかれば将来的には緩和されるかもしれません。
abstract 属性
abstractなクラスは、直接インスタンス化することができません。 他の、abstract でないクラスの基底クラスとしてのみインスタンス化できます。
クラスは、abstract属性付きで宣言された時か、 abstractと宣言された仮想メンバ関数を持っているときに abstractになります。
非仮想関数をabstractと宣言することはできません。
abstract と宣言された関数であっても、関数定義本体を書くことができます。 これは、必ずオーバーライドされる必要があるにせよ、 ‘クラスの基本共通機能’ を提供できるようにするためです。