C++のラムダ式で発生するコンパイラエラー C3493の原因と対策について解説
コンパイラ エラー C3493は、C++でラムダ式を使用する際、外部変数を暗黙的にキャプチャしようとすると発生します。
たとえば、キャプチャリストが空の場合に外部変数へアクセスするとエラーとなります。
対処法として、変数を明示的にキャプチャするか、[&]
のように既定のキャプチャモードを設定してください。
C3493エラーの背景と原因
ラムダ式における変数キャプチャの基本
C++のラムダ式は、外部の変数を関数のように利用する機能を持っています。
このとき、ラムダ式内で使用する外部変数は、キャプチャと呼ばれる仕組みを使ってラムダに渡す必要があります。
キャプチャには、明示的に変数名を記述する方法と、既定キャプチャモード(値渡しまたは参照渡し)を使う方法があります。
例えば、[]
という空のキャプチャリストは、外部変数を全くキャプチャしないため、ラムダ内で外部変数にアクセスしようとするとエラーが発生します。
既定キャプチャモードと空のキャプチャリストの違い
既定キャプチャモードは、[=]
(値渡し)または[&]
(参照渡し)で記述され、ラムダ内で利用する外部変数を自動的にキャプチャする指定方法です。
これに対して、空のキャプチャリスト[]
は、明示的にキャプチャする変数を何も指定しないため、ラムダ内で外部変数へのアクセスが不可能となります。
例えば、外部変数を変更したい場合には参照渡しか、アクセスのみであれば値渡しかの指定が必要です。
エラーメッセージ C3493 の意味
コンパイラが出力するエラーメッセージ「既定のキャプチャ モードが指定されていないため、’var’ を暗黙的にキャプチャできません」は、キャプチャリストにキャプチャ方法が記述されていないため、ラムダ式内で外部変数var
にアクセスできない状況を示しています。
つまり、変数を利用する場合は、既定キャプチャモードでキャプチャするか、変数名を明示的に記述する必要があるということです。
発生するケースとコード例
空のキャプチャリスト使用時のエラーケース
空のキャプチャリスト[]
を使用すると、ラムダ式は外部変数をキャプチャしません。
そのため、外部変数を参照または変更するコードを書くと、コンパイラエラーが発生します。
これは、キャプチャリストを空にしている場合、意図しないキャプチャが行われることを防ぐための措置です。
明示的キャプチャ不足によるエラーケース
明示的にキャプチャすべき変数をキャプチャリストに記述しない場合も、コンパイラエラーが発生します。
例えば、ラムダ式内で変数の値を更新しようとする場合に、その変数がキャプチャリストに含まれていなければ、C3493エラーが発生します。
コード例:エラー発生例
以下のサンプルコードでは、外部変数m
をラムダ式内で更新しようとしていますが、キャプチャリストが空になっているためエラーが発生します。
#include <iostream>
int main() {
int m = 55; // 外部変数
// 空のキャプチャリストなので、mはキャプチャされずエラーとなる
[](int n) { m = n; }(99); // C3493エラーが発生します
std::cout << "m = " << m << std::endl;
return 0;
}
コンパイル時にエラー:
error C3493: 既定のキャプチャ モードが指定されていないため、'm' を暗黙的にキャプチャできません
コード例:修正例
以下のサンプルコードは、既定キャプチャモードを参照渡し[&]
で指定することでエラーが解消された例です。
#include <iostream>
int main() {
int m = 55; // 外部変数
// 既定キャプチャモードとして参照渡しを指定しているので、mがキャプチャされる
[&](int n) { m = n; }(99);
std::cout << "m = " << m << std::endl;
return 0;
}
m = 99
エラー解決の対策
既定キャプチャモードの指定方法
キャプチャが必要な場合は、既定キャプチャモードを用いることで外部変数を自動的にキャプチャすることが可能です。
既定キャプチャモードには、値渡し[=]
と参照渡し[&]
があり、それぞれの用途に応じて使い分けることが重要です。
引用渡しによる指定方法
外部変数の値を直接利用する際に、変数の変更をラムダ外に反映させたくない場合は、引用渡し(参照渡し)を用います。
以下のサンプルコードは、参照渡しを用いて外部変数をキャプチャする例です。
#include <iostream>
int main() {
int count = 10; // 外部変数
// 参照渡しでキャプチャするため、ラムダ式内での変更が元の変数に反映される
auto changeCount = [&](int delta) { count += delta; };
changeCount(5);
std::cout << "count = " << count << std::endl;
return 0;
}
count = 15
値渡しによる指定方法
外部変数の値をコピーしてラムダ式内で利用する場合は、値渡しを利用します。
値渡しの場合、ラムダ式内の変更は元の変数に影響を与えません。
以下のサンプルコードは、値渡しによって変数をキャプチャする例です。
#include <iostream>
int main() {
int value = 75; // 外部変数
// 値渡しでキャプチャするため、ラムダ式内での変更はコピーに対してのみ行われる
auto showValue = [=](int addend) {
int newValue = value + addend;
std::cout << "newValue = " << newValue << std::endl;
};
showValue(25);
std::cout << "value = " << value << std::endl;
return 0;
}
newValue = 100
value = 75
明示的な変数キャプチャの記述方法
場合によっては、必要な変数のみを明示的にキャプチャリストに記述することも有効です。
たとえば、複数の外部変数が存在する場合、全てを既定キャプチャすると保守性が低下する恐れがあります。
必要な変数だけをキャプチャすることで、意図しない副作用を防ぐことができます。
以下に明示的な記述の例を示します。
#include <iostream>
int main() {
int a = 10;
int b = 20;
// 変数aだけを参照渡しでキャプチャする
auto calculate = [&a](int x) {
int result = a + x;
std::cout << "result = " << result << std::endl;
};
calculate(b);
std::cout << "a = " << a << std::endl;
return 0;
}
result = 30
a = 10
実装時の注意事項
キャプチャ動作の理解と確認ポイント
ラムダ式で変数をキャプチャする際は、どの方法でキャプチャされるかを明確に理解しておくことが大切です。
特に以下の点に注意してください。
- 既定キャプチャモードを使用する場合、全ての使用する外部変数が自動的にキャプチャされるため、不要な変数もキャプチャされる可能性がある。
- 明示的なキャプチャを用いるときは、キャプチャリストが正しく記述されているか確認する。
- 値渡しの場合、ラムダ内で行われた変更は元の変数に影響を与えないため、その挙動を理解する。
これらのポイントを事前に確認することで、エラー回避が容易になります。
修正方法選択時の考慮事項
実装時に、値渡しと参照渡しをどちらでキャプチャするかは、プログラムの目的に応じて慎重に選ぶ必要があります。
- 参照渡しは、外部変数の最新の状態にアクセスできる利点がありますが、意図しない副作用を招く可能性があるため、変更可能な変数には注意が必要です。
- 値渡しは、変数の値を安全にコピーできますが、ラムダ式内での更新が元の変数に反映されない点を意識する必要があります。
また、複数の変数を扱う場合、明示的にキャプチャすべき変数と既定キャプチャモードを併用するシーンを明確にし、コードの可読性と保守性を保つことが大切です。
まとめ
この記事では、C++のラムダ式において、外部変数をどのようにキャプチャするかの基本と、キャプチャ方法が不足した場合に発生するC3493エラーの背景を解説しています。
空のキャプチャリストや明示的キャプチャ不足によるエラー発生例と、その修正方法として参照渡しや値渡しを用いた対策をサンプルコード付きで示し、エラー解消に必要なポイントを詳しく述べています。