编译器错误 C2065

“identifier”:未声明的标识符

编译器找不到标识符的声明。 此错误有许多可能的原因。 C2065 的最常见原因是标识符未声明、标识符拼写错误、声明标识符的标头未包含在文件中,或标识符缺少范围限定符,例如指定了 cout 而不是 std::cout。 有关 C++ 中的声明的详细信息,请参阅声明和定义 (C++)

下面更详细地描述了一些常见问题和解决方法。

标识符未声明

如果标识符是变量或函数名称,则在使用它之前必须先声明它。 函数声明还必须包含其参数的类型,然后才能使用该函数。 如果使用 auto 声明变量,编译器必须能够从其初始化表达式推理类型。

如果标识符是类或结构的成员,或者在命名空间中声明,则在结构、类或命名空间范围之外使用时,它必须由类或结构名称或者命名空间名称限定。 或者,必须通过 using 指令(例如 using namespace std;)将命名空间引入范围,或必须通过 using 声明(例如 using std::string;)将成员名称引入范围。 否则,未限定的名称被视为当前范围内未声明的标识符。

如果标识符是用户定义的类型的标记(例如 classstruct),则必须先声明标记的类型,然后才能使用该标识符。 例如,声明 struct SomeStruct { /*...*/ }; 必须事先存在,才能在代码中声明变量 SomeStruct myStruct;

如果标识符是类型别名,则必须先通过 using 声明或 typedef 来声明类型,然后才能使用该标识符。 例如,必须先声明 using my_flags = std::ios_base::fmtflags;,然后才能将 my_flags 用作 std::ios_base::fmtflags 的类型别名。

示例:拼写错误的标识符

当标识符名称拼写错误或标识符使用错误的大写和小写字母时,通常会发生此错误。 声明中的名称必须与使用的名称完全匹配。

// C2065_spell.cpp
// compile with: cl /EHsc C2065_spell.cpp
#include <iostream>
using namespace std;
int main() {
    int someIdentifier = 42;
    cout << "Some Identifier: " << SomeIdentifier << endl;
    // C2065: 'SomeIdentifier': undeclared identifier
    // To fix, correct the spelling:
    // cout << "Some Identifier: " << someIdentifier << endl;
}

示例:使用无范围标识符

如果标识符未正确限定范围,则可能会发生此错误。 如果在使用 cout 时看到 C2065,则原因是范围有问题。 如果 C++ 标准库函数和运算符未完全由命名空间限定,或者你未使用 using 指令将 std 命名空间引入当前范围,则编译器将找不到它们。 若要解决此问题,必须完全限定标识符名称,或使用 using 指令指定命名空间。

此示例无法编译,因为 coutendl 是在 std 命名空间中定义的:

// C2065_scope.cpp
// compile with: cl /EHsc C2065_scope.cpp
#include <iostream>
// using namespace std;   // Uncomment this line to fix

int main() {
    cout << "Hello" << endl;   // C2065 'cout': undeclared identifier
                               // C2065 'endl': undeclared identifier
    // Or try the following line instead
    std::cout << "Hello" << std::endl;
}

classstructenum class 类型内部声明的标识符在该范围之外使用时,也必须通过其封闭范围的名称进行限定。

示例:预编译标头不是第一个指令

如果将任何预处理器指令(例如 #include#define#pragma)放在预编译头文件的 #include 之前,则可能会发生此错误。 如果源文件使用预编译头文件(即,如果该文件是使用 /Yu 编译器选项编译的),则会忽略预编译头文件之前的所有预处理器指令。

此示例无法编译,因为 coutendl 是在 <iostream> 标头中定义的,而该标头包含在预编译头文件之前,因此将被忽略。 若要生成此示例,请创建所有三个文件,然后依次编译 pch.h(某些 Visual Studio 版本使用 stdafx.cpp)和 C2065_pch.cpp

// pch.h (stdafx.h in Visual Studio 2017 and earlier)
#include <stdio.h>

pch.hstdafx.h 源文件:

// pch.cpp (stdafx.cpp in Visual Studio 2017 and earlier)
// Compile by using: cl /EHsc /W4 /c /Ycstdafx.h stdafx.cpp
#include "pch.h"

源文件 C2065_pch.cpp

// C2065_pch.cpp
// compile with: cl /EHsc /W4 /Yustdafx.h C2065_pch.cpp
#include <iostream>
#include "stdafx.h"
using namespace std;

int main() {
    cout << "Hello" << endl;   // C2065 'cout': undeclared identifier
                               // C2065 'endl': undeclared identifier
}

若要解决此问题,请将 <iostream> 的 #include 添加到预编译头文件中,或者在源文件中包含预编译头文件后移动该指令。

示例:缺少头文件

如果未包含声明标识符的头文件,则可能会发生错误。 确保包含标识符声明的文件包含在使用它的每个源文件中。

// C2065_header.cpp
// compile with: cl /EHsc C2065_header.cpp

//#include <stdio.h>
int main() {
    fpos_t file_position = 42; // C2065: 'fpos_t': undeclared identifier
    // To fix, uncomment the #include <stdio.h> line
    // to include the header where fpos_t is defined
}

另一个可能的原因是使用了初始化表达式列表但未包含 initializer_list 标头<>。

// C2065_initializer.cpp
// compile with: cl /EHsc C2065_initializer.cpp

// #include <initializer_list>
int main() {
    for (auto strList : {"hello", "world"})
        if (strList == "hello") // C2065: 'strList': undeclared identifier
            return 1;
    // To fix, uncomment the #include <initializer_list> line
}

如果定义 VC_EXTRALEANWIN32_LEAN_AND_MEANWIN32_EXTRA_LEAN,可能会在 Windows 桌面应用源文件中看到此错误。 这些预处理器宏从 windows.hafxv_w32.h 中排除一些头文件以加快编译速度。 在 windows.hafxv_w32.h 中查看排除项的最新说明。

示例:缺少右引号

如果字符串常量的后面缺少右引号,则可能会发生此错误。 这样很容易迷惑编译器。 报告的错误位置之前的几行可能缺少右引号。

// C2065_quote.cpp
// compile with: cl /EHsc C2065_quote.cpp
#include <iostream>

int main() {
    // Fix this issue by adding the closing quote to "Aaaa"
    char * first = "Aaaa, * last = "Zeee";
    std::cout << "Name: " << first
        << " " << last << std::endl; // C2065: 'last': undeclared identifier
}

示例:在 for 循环范围之外使用迭代器

如果在 for 循环中声明迭代器变量,然后尝试在 for 循环范围之外使用该迭代器变量,则可能会发生此错误。 编译器默认会启用 /Zc:forScope 编译器选项。 有关详细信息,请参阅调试迭代器支持

// C2065_iter.cpp
// compile with: cl /EHsc C2065_iter.cpp
#include <iostream>
#include <string>

int main() {
    // char last = '!';
    std::string letters{ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" };
    for (const char& c : letters) {
        if ('Q' == c) {
            std::cout << "Found Q!" << std::endl;
        }
        // last = c;
    }
    std::cout << "Last letter was " << c << std::endl; // C2065
    // Fix by using a variable declared in an outer scope.
    // Uncomment the lines that declare and use 'last' for an example.
    // std::cout << "Last letter was " << last << std::endl; // C2065
}

示例:预处理器已删除声明

如果引用条件编译代码中的函数或变量,而该代码不是为当前配置编译的,则可能会发生此错误。 如果在头文件中调用生成环境当前不支持的函数,则也可能会发生该错误。 如果某些变量或函数仅在定义了特定的预处理器宏时才可用,请确保调用这些函数的代码只能在定义了相同的预处理器宏时编译。 在 IDE 中可以轻松辨识此问题:如果未为当前生成配置定义所需的预处理器宏,则函数的声明将灰显。

下面是在“调试”中生成时有效,但在“发布”时无效的代码示例:

// C2065_defined.cpp
// Compile with: cl /EHsc /W4 /MT C2065_defined.cpp
#include <iostream>
#include <crtdbg.h>
#ifdef _DEBUG
    _CrtMemState oldstate;
#endif
int main() {
    _CrtMemDumpStatistics(&oldstate);
    std::cout << "Total count " << oldstate.lTotalCount; // C2065
    // Fix by guarding references the same way as the declaration:
    // #ifdef _DEBUG
    //    std::cout << "Total count " << oldstate.lTotalCount;
    // #endif
}

示例:C++/CLI 类型推导失败

如果无法从使用的参数中推导预期的类型参数,则在调用泛型函数时可能会发生此错误。 有关详细信息,请参阅泛型函数 (C++/CLI)

// C2065_b.cpp
// compile with: cl /clr C2065_b.cpp
generic <typename ItemType>
void G(int i) {}

int main() {
   // global generic function call
   G<T>(10);     // C2065
   G<int>(10);   // OK - fix with a specific type argument
}

示例:C++/CLI 属性参数

此错误也可能起因于为 Visual Studio 2005 执行的编译器遵从性工作:Visual C++ 属性的参数检查。

// C2065_attributes.cpp
// compile with: cl /c /clr C2065_attributes.cpp
[module(DLL, name=MyLibrary)];   // C2065
// try the following line instead
// [module(dll, name="MyLibrary")];

[export]
struct MyStruct {
   int i;
};