はじめに
この記事は、C++ Advent Calendar 2018 の13日目です。
前日の記事は @Linus_MK さん
明日の記事は @shohirose さんです。
属性とは
属性はコンパイラに追加の情報を伝えるための構文で、[[attributes]] といった書き方をする。
最適化や、警告の追加や抑制等ができる。
C++11
noreturn 属性
関数
関数が決して返らないことを示す属性。
例外送出やstd::exit, std::abortのラッパー関数に付ける。
これにより関数が返らないパスがある場合の警告を抑制する。
[[noreturn]] void exit()
{
	std::exit(0);
}
int hoge(int x)
{
	if (x > 0) {
		return x;
	} // ifに入らない場合returnがないが
	exit(); // この関数は返らない
}
carries_dependency属性
関数 関数パラメータ
並列プログラミングのアトミック操作において、値に依存した順序付けを関数をまたいで伝搬することを示す属性。
atomicやmemory_orderについてはここでは詳しく述べない。 (使ったことがないので詳しく知らない)
struct Hoge 
{
	int hoge = 0;
};
std::atomic<Hoge*> x(nullptr);
[[carries_dependency]] Hoge* is_load()
{
	return x.load(std::memory_order_consume);
}
int main()
{
	std::thread th([]() {
		x.store(new Hoge{ 100 }, std::memory_order_release);
	});
	Hoge* pHoge = is_load();
	if (pHoge)
	{
		std::cout << pHoge->hoge;
	}
	th.join();
	return 0;
}
C++14
deprecated属性
クラス タイプエイリアス  列挙子 関数 テンプレート特殊化
変数 非静的メンバ変数  列挙型(C++17) 名前空間(C++17)
対象の機能が非推奨であることを示す属性。
API開発時等で非推奨になった機能に使用することで、ユーザーに警告として示すことができる。
引数として表示する警告文を指定することができる。
deprecated("reason")
[[deprecated("please use new_func() function")]]
void old_func(){}
void new_func(){}
int main()
{
	old_func(); // warning
	return 0;
}
C++17
fallthrough属性
switch-case
switch文においてフォールスルーであることを明示する属性。
意図的なフォールスルーの箇所に指定することで警告の抑制ができる。
int hoge(int i)
{
	int ret = 0;
	switch (i)
	{
	case 1: 
		ret += 1;
		[[fallthrough]]; // 意図的なフォールスルー
	case 2:
		ret += 2; 
		break;
	}
	return ret;
}
maybe_unused属性
クラス タイプエイリアス 列挙型 関数 関数パラメータ
変数 非静的メンバ変数 列挙子
未使用なものを明示する属性。
意図的に未使用なものに指定することで警告の抑制ができる。
void hoge([[maybe_unused]]int _unused)
{
	// _unusedは使用されない
}
nodiscard属性
関数
関数の戻り値を破棄してはならないことを示す属性。
破棄された場合警告文がでる。
引数として表示する警告文を指定することができる。
nodiscard("reason") (C++20以降)
[[nodiscard]]int * create()
{
	return new int(0);
}
int main()
{
	create(); // warning
	return 0;
}
C++20
no_unique_address属性
非静的メンバ変数
データメンバがそのクラスの他のすべての非静的データメンバと異なるアドレスを持つ必要がないことを示す属性
struct Empty{};
struct Test
{
	int x;
	Empty _noused;
};
struct Test2
{
	int x;
	[[no_unique_address]] Empty _noused;
};
int main()
{
	static_assert(sizeof(Empty) >= 1);
	static_assert(sizeof(Test) >= sizeof(int) + 1);
	static_assert(sizeof(Test2) == sizeof(int));
	return 0;
}
likely, unlikely属性
if switch-case
条件分岐で頻度を示す属性。
分岐が選ばれやすい場合はlikely、選ばれにくい場合はunlikelyを指定することで最適化のヒントにする。
	if (normal())[[likely]]
	{
		// 良く通る処理
	}
	if (rare())[[unlikely]]
	{
		// 滅多にとおらない処理
	}
C++23
assume属性
空文
関数が満たすべき仮定を指定することで、最適化のヒントとする。
引数に渡す式は効果がないが、評価結果がfalseの場合は未定義動作になる
int f(int y)
{
  // 効果を持たないため変数 y の値は変化しない。
  [[assume(++y == 43)]];
  // 最適化により return 42; へと置換される可能性がある。
  return y;
}
C++26以降
pre, post, assert属性(?)
契約プログラミングのための属性。まとまるまでは保留
old
expects, ensures, assert属性
関数
以下まだ勉強不足なのでさらっと見た感じ
むしろ教えて欲しい。。
関数に対して事前条件、事後条件、アサーションの契約を指定する属性。
契約に違反した場合std::contract_violationが生成されハンドラに渡される。
構文
[[expects contract-level: expression]]
[[ensures contract-level identifier: expression]]
[[assert contract-level: expression]]
- contract-level: 契約レベル(後で記述)
- expression: boolに変換可能な式
- identifier: 関数の戻り値を表すための識別子
contract-level
default
特に指定がない場合これになる。
実行時チェックのコストが関数を実行するコストと比較して小さいと想定される場合使用。
audit
実行時チェックのコストが関数を実行するコストと比較して大きいと想定される場合使用。
axiom
形式的なコメントであり、実行時チェックをしない場合に使用。
ただし、コード静的解析ツールのために利用されるそう。
expect属性
関数に入る際の、その引数またはその他のオブジェクトの状態に期待することを定義。
// 引数には正の数のみ許容
int f(int x)[[expects: x > 0]];
ensures属性
関数から出る際の、その戻り値またはその他のオブジェクトの状態に期待することを定義。
// 戻り値には正の数のみ許容
int g(int x)[[ensures ret: ret > 0]];
assert属性
関数本体内で、満たされるべき条件を定義。
他二つとは記述場所が違う点注意。
今後assertマクロの代わりになりそうな認識でいる。
void p(int x,int y)
{
	// do something...
	[[assert:x + y > 0]];
	// do something...
}
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0542r0.html
さいごに
今回は属性をまとめてみました。
C++20のやつ等まだ使ったことないやつは自信もないので、間違っている点等ありましたら教えていただけると幸いです。
