3回目の今回は属性。
属性(attributes)は、ソースコードに対して追加の情報をコンパイラに伝えるための構文。
C++11で[[noreturn]],[[carries_dependency]]が追加されていました。
その属性でC++17において追加されていたものがありますので見ていきます。
・[[fallthrough]]属性
switch-case文でよくある
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
switch (a) { case 1: case 2: std::cout << "case 1 or 2" << std::endl; break; case 3: std::cout << "case 3" << std::endl; break; case 4: std::cout << "case 4" << std::endl; break; default: break; } |
こんな感じのコード。
caseの1と2で同じ処理を行いたい場合こんな感じで記述することがありますが賢いコンパイラーさんは「おい、処理が突き抜けているぞ!」と(warning: this statement may fall through)警告を出してくれます。
そこで警告をうざいと感じて警告を出さないようにしていまうと本当に気づきたいときに出ない、というオチになったり。。
ということでこの[[fallthrough]]は「これはあえてそうしているのだ!警告は無用だ」と教えてあげることができると。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
switch (a) { case 1: case 2: std::cout << "case 1 or 2" << std::endl; [[fallthrough]]; case 3: std::cout << "case 3" << std::endl; break; case 4: std::cout << "case 4" << std::endl; break; default: break; |
上記のようにcase2のパターンで[[fallthrough]];を書いてあげると警告は出ない、case1の場合だとそもそも[[fallthrough]];はいらない
C#なんかフォールスルー自体禁止している言語もあるので使わないのがベストか(case1,case2の連続するパターンの場合はC#でもOK)
・[[maybe_unused]]属性
使用しない可能性のある変数に対する警告を抑制するためのもの
そんなもの何に使うんだとと思うがassert使用時とかか。
たとえば
1 2 3 4 5 6 7 8 9 10 11 |
#define NDEBUG #include <cassert> void boolfunc(bool a) { assert(a); } int main() { boolfunc(false); } |
上記の場合boolfunc関数の引数aをassertに指定しているが#define NDEBUGが定義されているので無効となります。なのでaは未使用として
1 |
warning: unused parameter 'a' [-Wunused-parameter] |
てな感じででちゃう。
そこで
1 2 3 4 |
void boolfunc([[maybe_unused]] bool a) { assert(a); } |
てやると警告はでなくなる。
・[[nodiscard]]属性
関数の戻り値を破棄してはダメ、破棄した場合は警告を発生させる
ある関数で処理が行われたとき、エラーだった場合falseなり値を返すがその値を呼び出し側が使用していない。
1 2 3 4 5 6 7 8 9 10 11 |
#include <iostream> int func() { std::cout << "1です" << std::endl; return 1; } int main() { func(); } |
上記のようなコードがあった場合(もう少しましなものがよかったが)このコードでは警告は発生しない。
しかし
1 2 3 4 5 |
[[nodiscard]] int func() { std::cout << "1です" << std::endl; return 1; } |
とすれば
1 |
warning: ignoring return value of 'int func()', declared with attribute nodiscard |
という感じで警告を発してくれる。
静的解析ツールとかでいい感じに警告は発してくれるだろうが警告をだせるようになった。
・名前空間と列挙子への属性付加を許可
1 2 3 4 |
enum EType { E_TYPE1 [[maybe_unused]], } namespace [[maybe_unused]] space{} |
こんな感じでnamespaceやenumにも属性が書けるようになった。
・属性の名前空間指定に繰り返しをなくす
なんのこっちゃと思いましたが
これが
1 |
[[CC::opt(1), CC::debug]] void f() {} |
こうなる
1 |
[[ using CC: opt(1), debug ]] void f() {} |
ただしusing指定できるのは一つだけのよう。あとusingと名前空間の指定は混在できないとのこと
1 2 |
// これはダメ [[using CC: CC::opt(1)]] void j() {} |
まあ混在できるとややこしいからいいか。
・不明な属性を無視する
1 2 |
[[gnu::deprecated]] void func(); |
GCC/Clangではgnu::deprecated属性が使用されそれ以外では無視される。
これによっていちいち#ifdefとかでコンパイラごとに考慮するようなことは必要がなくなった。
というわけでC++17で変わった使わないようで結構使うかもしれない属性関連でした。