Microsoft Visual C++のコンパイラエラー C3480について解説 ~ラムダ式キャプチャの注意点と対策~
Microsoft のコンパイラエラー C3480 は、ラムダ式のキャプチャで対象となる変数が外側の関数スコープに存在しない場合に発生します。
C++の環境では、ラムダ式内で正しく変数を参照するため、外側で宣言された変数のみをキャプチャするよう記述する必要があります。
なお、C言語にはラムダ構文がないため、このエラーは主にC++で確認されます。
エラーの基本情報
C3480エラーの概要
C3480エラーは、ラムダ式のキャプチャリストに記述された変数が、外側の関数スコープに存在しない場合に発生します。
具体的には、ラムダ式がキャプチャする変数は関数内で定義されたローカル変数でなければならないというルールがあります。
エラーメッセージでは「’var’: ラムダ キャプチャ変数は、外側の関数スコープの変数である必要があります」と表示されるため、キャプチャ対象として適切な変数かどうかを確認する必要があります。
ラムダ式におけるキャプチャ変数の役割
ラムダ式は、関数内で定義された式や関数オブジェクトとして利用される構文です。
キャプチャ変数は、ラムダ式内で外部の変数(関数スコープ内の変数)を利用するための仕組みです。
キャプチャには主に以下の2種類があります。
- 値渡しキャプチャ
- 参照渡しキャプチャ
たとえば、関数内に定義された変数をラムダ式内で操作する場合、適切にキャプチャすることで、ラムダ式がその変数の値にアクセスできるようになります。
しかし、グローバル変数や関数内に存在しない変数をキャプチャリストに記述すると、C3480エラーが発生するため注意が必要です。
発生原因と具体例
外側の関数スコープの必要性
ラムダ式でキャプチャされる変数は、必ず外側の関数スコープ内に存在する必要があります。
これは、ラムダ式が定義された関数のローカルな変数にアクセスするための仕組みとなるためです。
変数がグローバルであったり、関数スコープ以外のスコープにある場合、その変数はキャプチャの対象として認識されず、C3480エラーが発生します。
不適切なキャプチャリストの例
次のサンプルコードは、グローバル変数をキャプチャリストに記述しているため、C3480エラーが発生する例です。
#include <iostream>
// グローバル変数(関数スコープではない)
int global = 0;
int main()
{
// グローバル変数をキャプチャリストに含めるとエラーが発生する
[&global] { global = 5; }();
std::cout << "global = " << global << std::endl;
return 0;
}
(コンパイル時にエラー:'global': ラムダ キャプチャ変数は、外側の関数スコープの変数である必要があります)
上記のコードでは、グローバル変数 global
をキャプチャリスト [&global]
に含めているため、エラーが発生します。
エラー発生パターンの詳細解説
C3480エラーは、キャプチャリストに記載された変数が、コンパイラにとって外側の関数スコープ内に存在しないと判断されたときに発生します。
具体的には以下のようなケースがあります。
- グローバル変数や名前空間スコープに存在する変数を、誤ってラムダ式のキャプチャリストに含めた場合
- クラスメンバー変数を静的メンバーではなく、普通のメンバー変数としてキャプチャしようとした場合
数式で表現すると、ラムダ式がアクセス可能な変数の集合
となります。
このルールに違反するとエラーとなり、修正が必要です。
エラー回避および解決方法
キャプチャリストからの変数削除方法
エラーを回避する最も簡単な方法は、キャプチャリストから問題となる変数を削除することです。
特にグローバル変数の場合は、キャプチャする必要がないため、キャプチャリストから外すことでエラーを防げます。
以下に、エラー発生前と修正後のコードの比較を示します。
修正前後のコード比較
修正前のコード(エラー発生)
#include <iostream>
// グローバル変数
int global = 0;
int main()
{
// グローバル変数をキャプチャするとエラーが発生する
[&global] { global = 5; }();
std::cout << "global = " << global << std::endl;
return 0;
}
(コンパイル時エラー:'global': ラムダ キャプチャ変数は、外側の関数スコープの変数である必要があります)
修正後のコード
#include <iostream>
// グローバル変数
int global = 0;
int main()
{
// キャプチャリストから global を削除してエラーを回避する
[] { global = 5; }();
std::cout << "global = " << global << std::endl;
return 0;
}
global = 5
上記の修正後のコードでは、キャプチャリストが空になっているため、グローバル変数 global
はラムダ式内で通常の変数として参照され、エラーが解消されます。
正しいキャプチャ記述方法の実践例
関数内のローカル変数をラムダ式で利用する場合は、正しくキャプチャリストに記述することで、意図した動作が得られます。
以下は、関数スコープに存在するローカル変数 localVar
を参照渡しキャプチャする正しい例です。
#include <iostream>
int main()
{
int localVar = 10; // 関数内のローカル変数
// ローカル変数をキャプチャして、ラムダ式内で値を更新する
[&localVar] {
localVar += 5; // ローカル変数にアクセス
}();
std::cout << "localVar = " << localVar << std::endl;
return 0;
}
localVar = 15
この例では、localVar
は main
関数内に定義されたローカル変数であるため、ラムダ式のキャプチャリストに正しく記述することができます。
参照渡しキャプチャを用いることで、ラムダ式内での変更が localVar
に反映され、期待通りの動作となります。
まとめ
本記事では、Microsoft Visual C++のコンパイラエラーC3480について解説しています。
ラムダ式で外側の関数スコープ内の変数だけがキャプチャ可能なルールと、その違反がエラーを引き起こす仕組みについて説明しました。
具体例とコード比較を通して、グローバル変数など不適切なキャプチャリストの修正方法と、正しいローカル変数のキャプチャ例を確認でき、エラー回避のためのポイントが整理されました。