Standard attributes(since C++11)
独断と偏見による個人的に重要そうなattribute達のメモ。
noreturn
void report(){throw "error";} int f(int x) { if(x>0)return x; report(); } int main() { f(1); }
例外送出をラップしたこのようなコードにおいては、コンパイラはその関数が返らないことを認識できない。(ラップせず直接書き込むと認識する)そのため、report()の呼び出しの後ろに決して実行されることのない無意味なreturn文を書かない限り、コンパイラは、このパスにreturn文が書かれていないという警告を出力する。noreturn属性は、その関数が返らない事を認識させるものである。
[[noreturn]] void report(){throw "error";} int f(int x) { if(x>0)return x; report(); } int main() { f(1); }
因みにGCC6.1.0では独自拡張だろうか、noreturn属性を含めずとも警告は出力されない。
$ g++ --version g++ (GCC) 6.1.0 Copyright (C) 2016 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ cat a.cpp void report(){throw "error";} int f(int x) { if(x>0)return x; report(); } int main() { f(1); } $ g++ -std=c++1z a.cpp
また、noreturn属性のつけられた関数はstd::abort,std::exit,std::quick_exit,std::unexpected,std::terminate,std::rethrow_exception,
std::throw_with_nested,std::nested_exception::rethrow_nestedか、例外処理のみを許す事になる。
$ cat test.cpp #include<stdexcept> [[noreturn]] void g(int i) { if(i>0)throw std::runtime_error("positive"); //else std::exit(i); } int main() { g(1); } $ g++ -std=c++1z test.cpp test.cpp: 関数 ‘void g(int)’ 内: test.cpp:7:1: 警告: ‘noreturn’ 関数が戻り (return) ます
しかしこの警告文は適切でない気がする。
参考
nodiscard,fallthrough
nodiscardとfallthroughは以前まとめた。
deprecated
非推奨を属性として設定できる。関数、クラス、typedef名、変数、非staticデータメンバー、関数、enum、テンプレートの特殊化に指定する事ができる。
$ cat test.cpp #include<utility> #include<vector> [[deprecated("inefficiently")]] std::vector<int> heavy_add_vector(std::vector<int> arg) { for(auto& e:arg)e+=1; return arg; } std::vector<int> add_vector(std::vector<int> arg) { for(auto& e:arg)e+=1; return std::move(arg); } int main() { std::vector<int> v{1,2,3,4,5,},w(heavy_add_vector(v)),z(add_vector(std::move(v))); } $ g++ -std=c++1z test.cpp test.cpp: 関数 ‘int main()’ 内: test.cpp:18:35: 警告: ‘std::vector<int> heavy_add_vector(std::vector<int>)’ is deprecated: inefficiently [-Wdeprecated-declarations] std::vector<int> v{1,2,3,4,5,},w(heavy_add_vector(v)),z(add_vector(std::move(v))); ^~~~~~~~~~~~~~~~ test.cpp:4:50: 備考: ここで宣言されています [[deprecated("inefficiently")]] std::vector<int> heavy_add_vector(std::vector<int> arg) ^~~~~~~~~~~~~~~~ test.cpp:18:53: 警告: ‘std::vector<int> heavy_add_vector(std::vector<int>)’ is deprecated: inefficiently [-Wdeprecated-declarations] std::vector<int> v{1,2,3,4,5,},w(heavy_add_vector(v)),z(add_vector(std::move(v))); ^ test.cpp:4:50: 備考: ここで宣言されています [[deprecated("inefficiently")]] std::vector<int> heavy_add_vector(std::vector<int> arg)
(GCC6.1.0はかなりRVOが効くので実際のところ分からないが。参考:Return value optimization - Wikipedia, the free encyclopedia)
$ g++ --version g++ (GCC) 6.1.0 Copyright (C) 2016 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ cat a.cpp #include<iostream> struct A{ A(){} A(const A&){std::cout<<"A copy was made"<<std::endl;} }; A f(){return A();} int main() { A a=f(); std::cout<<"done\n"; } $ g++ a.cpp $ ./a.out done
maybe_unused
名前が使われないことをヒントとして示すattribute。assertはプリプロセッサーマクロであるため、リリースビルドで消えてしまう。そのためコンパイラからは変数bが使用されていないように見え、変数が使われていないという警告を出す可能性がある。これをmaybe_unused属性を付与する事により黙らせる事ができる。
[[maybe_unused]] void f([[maybe_unused]] bool thing1, [[maybe_unused]] bool thing2) { [[maybe_unused]] bool b = thing1 && thing2; assert(b); // in release mode, assert is compiled out, and b is unused // no warning because it is declared [[maybe_unused]] } // parameters thing1 and thing2 are not used, no warning
参考
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0212r1.pdf