コンパイラエラー

C言語 コンパイラエラー C3495 の原因と対策について解説

コンパイラ エラー C3495は、C++のラムダ式で自動ストレージ存続期間が必要な変数をキャプチャしようとする際に発生します。

staticやexternで宣言された変数をキャプチャリストに含めるとエラーになるため、注意が必要です。

この記事では原因と簡単な対応方法について解説します。

エラーの発生状況と背景

C3495 エラーの定義

C3495 エラーは、ラムダ式内でキャプチャ対象の変数に自動ストレージ存続期間が無い場合に発生します。

具体的には、変数が staticextern として宣言されている場合、その変数をラムダキャプチャで指定するとこのエラーが発生します。

エラーメッセージには以下のように表示されます。

'var': ラムダ キャプチャには自動ストレージ存続期間が指定されている必要があります

エラーが発生する条件

C3495 エラーは、ラムダ式における変数キャプチャのルールに反する場合に発生します。

具体的には、キャプチャ対象の変数が自動ストレージ存続期間を持っていない、つまり関数内で定義される通常の変数ではなく、staticextern 宣言されている変数である場合です。

ラムダ式における変数キャプチャの要件

ラムダ式で変数をキャプチャする際には、変数が関数実行時のスタック上に確保される自動ストレージ型である必要があります。

これにより、ラムダ式が呼び出された時点で正しい値が参照できるようになります。

staticextern変数はプログラム全体で一意の領域に格納されるため、自動ストレージ型とは異なり、キャプチャ対象としては適していません。

自動ストレージとstatic/extern変数の違い

自動ストレージ変数は通常、関数内で宣言され、関数の呼び出しとともに生成されるメモリです。

このため、関数の終了とともに破棄されます。

一方、static変数は関数の呼び出しに関係なくプログラムの実行中ずっと存在し、初期化も一度だけ行われます。

extern変数は別のソースファイルで宣言されたグローバル変数を参照するため、プログラム全体で共有されます。

これらの変数は自動ストレージではないため、ラムダ式のキャプチャ対象としては不適切となります。

ラムダ式と変数キャプチャの基本

ラムダ式の基本構文

ラムダ式は、関数オブジェクトを簡単に作成できる構文です。

基本的な構文は以下の通りです。

#include <iostream>
int main() {
    int localVar = 100;  // 自動ストレージ変数
    auto lambda = [localVar]() {  // 値キャプチャ
        // キャプチャした変数を使用
        std::cout << "localVar = " << localVar << std::endl;
    };
    lambda();
    return 0;
}
localVar = 100

このように、ラムダ式は [キャプチャリスト](引数) -> 戻り値 { 本文 } という形式で記述します。

キャプチャリストには、使用する変数を値あるいは参照で指定します。

変数キャプチャのルール

ラムダ式内で変数を使用する場合、その変数はキャプチャリストを通して明示的に指定する必要があります。

変数のキャプチャには主に値渡しと参照渡しがあり、用途に応じて使い分けます。

自動ストレージ存続期間の重要性

キャプチャされる変数は自動ストレージ変数でなければなりません。

これは、ラムダ式が変数を正しくアクセスするために必要な条件です。

自動ストレージ変数は関数内で確保され、必要なスコープ内で生存するため、ラムダ式内でも安全に利用できます。

キャプチャリストの記述方法

キャプチャリストは、[] 内にカンマ区切りで変数を記述することで指定します。

必要に応じて、キャプチャ方法を明示するために &(参照キャプチャ)やそのまま記述(値キャプチャ)を行います。

例えば、以下の例では、localVar を値としてキャプチャし、refVar を参照としてキャプチャしています。

#include <iostream>
int main() {
    int localVar = 42;
    int refVar = 10;
    auto lambda = [localVar, &refVar]() {
        std::cout << "localVar = " << localVar << std::endl;
        std::cout << "refVar = " << refVar << std::endl;
    };
    lambda();
    return 0;
}
localVar = 42
refVar = 10

エラー原因の詳細解説

static変数のキャプチャによる問題

static変数はプログラム開始時に一度だけ初期化され、以降関数の呼び出しごとに同じメモリ空間を使用します。

そのため、static変数をラムダ式内でキャプチャしようとすると、自動ストレージ変数としての要件を満たさないため、C3495 エラーが発生します。

以下のサンプルコードはこの問題を示しています。

#include <iostream>
int main() {
    static int staticVar = 66;  // static変数
    // static変数をキャプチャしようとするとエラー発生
    auto lambda = [&staticVar]() {
        std::cout << "staticVar = " << staticVar << std::endl;
    };
    lambda();
    return 0;
}

このコードでは、staticVar を参照キャプチャしようとしていますが、コンパイル時に C3495 エラーが発生します。

extern変数のキャプチャによる問題

extern変数は他のソースファイルで定義されるグローバル変数を参照するため、同様に自動ストレージ変数ではありません。

このため、extern変数をラムダ式のキャプチャリストに含めることはできず、C3495 エラーとなります。

各ケースの具体例

以下の例は、extern変数をキャプチャしようとしてエラーが発生する場合です。

まず、外部で定義される変数を宣言するためにヘッダ部分で extern を使用します。

ファイル extern_var.h:

// extern_var.h
#ifndef EXTERN_VAR_H
#define EXTERN_VAR_H
extern int globalVar;  // extern変数の宣言
#endif

ファイル extern_var.cpp:

#include "extern_var.h"
int globalVar = 88;  // extern変数の定義

メインファイル:

#include <iostream>
#include "extern_var.h"
int main() {
    // extern変数をキャプチャしてラムダ式を定義
    auto lambda = [&globalVar]() {
        std::cout << "globalVar = " << globalVar << std::endl;
    };
    lambda();
    return 0;
}

この場合、コンパイラは globalVar をラムダキャプチャとして扱えず、C3495 エラーを出力します。

エラー対策と修正方法

エラー回避の基本方針

C3495 エラーを回避するためには、キャプチャ対象の変数が自動ストレージ変数であることを確認する必要があります。

staticextern変数をラムダ式のキャプチャリストから除外し、必要であれば関数内の自動変数に格納する方法を検討します。

変数自体の宣言を変更することで、エラーの回避が可能です。

修正例のコード解説

キャプチャリストからの変数除外方法

例えば、static変数をラムダ式内で使用する場合、キャプチャリストから除外し、関数外で参照するように変更する方法があります。

以下の例では、static変数をキャプチャリストに含めず、直接参照する形に修正しています。

#include <iostream>
static int staticVar = 66;  // static変数はキャプチャせずにグローバルで宣言
int main() {
    // キャプチャリストに staticVar を含めずにラムダ式を定義
    auto lambda = []() {
        // 外部に宣言された static変数を直接参照
        std::cout << "staticVar = " << staticVar << std::endl;
    };
    lambda();
    return 0;
}
staticVar = 66

適切な変数宣言の変更手順

別の方法として、staticextern変数の代わりに、関数内で必要な値を自動変数として保持し、その変数をキャプチャする方法があります。

以下はその一例です。

#include <iostream>
int main() {
    // static変数から自動変数へ変更
    int autoVar = 77;
    // 自動変数を値キャプチャでラムダ式に渡す
    auto lambda = [autoVar]() {
        std::cout << "autoVar = " << autoVar << std::endl;
    };
    lambda();
    return 0;
}
autoVar = 77

このように、変数の宣言を自動変数として変更することで、ラムダキャプチャに適したストレージ期間を持つ変数とすることができ、C3495 エラーを回避できます。

まとめ

本記事では、C3495 エラーの定義と発生条件、ラムダ式における自動ストレージ変数の重要性を解説しました。

static や extern変数がキャプチャ対象である場合の問題点や、適切な修正方法としてキャプチャリストから除外する方法と自動変数への変更手順を具体例を交えて説明しています。

これにより、エラー発生の原因と対策が明確に理解できる内容となっています。

関連記事

Back to top button
目次へ