はじめに
C++11以降、属性 [[...]]
という統一的な構文が導入されました。属性は、コードの様々な要素(関数、クラス、変数など)に付加情報を与え、コンパイラの最適化を助けたり、特定のコードの使われ方について警告を出させたりするためのものです。
属性は、プログラムの動作そのものを直接変更するものではありませんが、コードの安全性と品質を向上させる上で非常に役立ちます。この記事では、C++でよく使われる4つの標準属性について解説します。
1. [[noreturn]]
: 関数が戻らないことを示す
[[noreturn]]
は、その関数が呼び出し元に決して制御を返さない(例: 例外を投げて終了する、exit
を呼ぶなど)ことをコンパイラに伝えます。
サンプルコード
#include <iostream>
#include <stdexcept>
// [[noreturn]]属性: この関数は決してreturnしないことを示す
[[noreturn]] void exit_with_error(const std::string& message) {
std::cerr << "致命的なエラー: " << message << std::endl;
std::exit(EXIT_FAILURE);
}
void process_data(int value) {
if (value < 0) {
// この関数は戻ってこないので、この後のコードは実行されない
exit_with_error("値が負です。");
}
// コンパイラは、ここが常に実行されるとは限らないことを理解し、最適化に役立てる
std::cout << "処理成功" << std::endl;
}
メリット: コンパイラは、[[noreturn]]
の関数呼び出し以降のコードに制御が移らないことを知っているため、到達不能コードに関する警告を抑制したり、より良いコードを生成したりできます。
2. [[deprecated]]
: 非推奨であることを示す
[[deprecated]]
は、その関数やクラスなどが古い、あるいは非推奨であり、将来的に削除される可能性があることを示します。この属性が付いた要素を使用すると、コンパイラが警告を出します。
サンプルコード
// 理由を添えて非推奨であることを示す (C++14)
[[deprecated("古いAPIです。代わりに NewApiFunction() を使用してください。")]]
void OldApiFunction() {
std::cout << "古いAPIが呼ばれました。" << std::endl;
}
void NewApiFunction() { /* ... */ }
int main() {
// この行をコンパイルすると、警告が表示される
OldApiFunction();
return 0;
}
メリット: ライブラリの作者などが、ユーザーに対して、より新しい、安全な代替機能へ移行するように促すことができます。
3. [[maybe_unused]]
: 未使用でも警告しない
[[maybe_unused]]
(C++17) は、変数や引数が意図的に使われていない場合に、コンパイラによる「未使用の変数」警告を抑制します。
サンプルコード
// デバッグビルドでのみ使う引数など
void process(int data, [[maybe_unused]] bool is_debug_mode) {
int result = data * 2;
#if _DEBUG
if (is_debug_mode) {
std::cout << "デバッグモードで実行中" << std::endl;
}
#endif
// is_debug_modeは、リリースビルドでは未使用になるが、警告は出ない
}
メリット: テンプレートプログラミングや、ビルド構成によって使われたり使われなかったりする変数に対して、不要な警告が表示されるのを防ぎます。
4. [[nodiscard]]
: 戻り値を無視させない
[[nodiscard]]
(C++17) は、関数の戻り値を無視してはいけないことを示します。もし、この属性が付いた関数の戻り値を変数に受け取ったり、if
文で使ったりせずに無視すると、コンパイラが警告を出します。
サンプルコード
// 戻り値でエラー状態を返すため、無視してはいけない関数
[[nodiscard("エラーコードが返されます。必ずチェックしてください。")]]
bool connect_to_server() {
return false; // 失敗したと仮定
}
int main() {
// NG: 戻り値を無視しているため、コンパイラが警告を出す
connect_to_server();
// OK: 戻り値を使っている
if (!connect_to_server()) {
std::cerr << "接続に失敗しました。" << std::endl;
}
return 0;
}
メリット: エラーコードを返す関数など、戻り値のチェックが不可欠な関数の呼び出し元で、チェック漏れという重大なバグを防ぐことができます。
まとめ
今回は、C++のコードの品質と安全性を高めるための「属性」について解説しました。
[[noreturn]]
: 戻らない関数[[deprecated]]
: 非推奨の機能[[maybe_unused]]
: 未使用でもOKな変数[[nodiscard]]
: 無視してはいけない戻り値
これらの属性を適切に使うことで、コンパイラとの対話を増やし、より堅牢で、意図が明確なコードを書くことができます。