Rokiのチラ裏

学生による学習のログ

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) ます

しかしこの警告文は適切でない気がする。

参考

stackoverflow.com


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