LNK2001 在 VS 2013 (MSVC 18) 上,但不在 VS 2015 (MSVC 19) 上

Posted

技术标签:

【中文标题】LNK2001 在 VS 2013 (MSVC 18) 上,但不在 VS 2015 (MSVC 19) 上【英文标题】:LNK2001 on VS 2013 (MSVC 18) but not on VS 2015 (MSVC 19) 【发布时间】:2016-08-17 14:10:48 【问题描述】:

我使用早期版本的 Cocos2dx 编写游戏并使用 VS 2013 对其进行编译。请注意,我将 CMake 和 Qt Creator 与两个编译器版本一起使用。当 Cocos2dx v3.12 出来时,我决定在我的游戏中将 lib 升级到那个版本并开始使用 VS 2015。然后我开始收到这个错误:

QCardManager.cpp.obj:-1:错误:LNK2001:未解析的外部符号 “公共:静态类 QCard * __cdecl QCard::create(enum PLAYER,struct 问题 const *,enum CARD_TYPE,int const &)" (?create@QCard@@SAPAV1@W4PLAYER@@PBUQuestion@@W4CARD_TYPE@@ABH@Z)

我在使用 VS 2013 时并没有遇到这个错误。经过几个小时的调试,我找到了原因。

这是QCard的粗略声明:

#include "2d/CCSprite.h"
#include "CommonVariables.h"

class RandomPostureSprite;
class Question;
namespace cocos2d

class Label;


enum class CARD_TYPE 
    QUESTION,
    OPTION
;

class QCard : public cocos2d::Sprite

public:
    static QCard *create(PLAYER player, const Question *question, CARD_TYPE type, const int &index);

我在QCard.cpp 文件中正确实现了该功能,并且该文件也已正确添加到项目中。 所以问题是class Question; 前向声明。我在QCard.cpp 中包含了QuestionParser.h 文件,但由于我在QCard.h 中使用了QCard 的前向声明,QCardManager.cpp 文件没有Question 的实现,因此链接器错误。

这是我的问题:我意识到 VS 2015 所做的应该是预期的行为。但为什么会发生这种行为?相同的代码在 VS 2013 上编译没有错误,但在 VS 2015 上没有。我阅读了Breaking Changes in Visual C++ 2015 指南,但看不到任何相关内容。

编辑 1: 结果证明前向声明应该是struct Question 而不是class Question。当我尝试在QCardManager.cpp 中使用QCard::create 时,出现上述链接器错误。但不在同一目录中的TimerHUD.cpp 中。我将发布它们的摘要内容。请记住,我将 QCard 的声明与此编辑保持一致。

问题结构,位于QuestionParser.h:

struct Question 
    Question()
        : type()
        , source()
        , alias()
        , color(0, 0, 0)
    
;

QCardManager.h

// Cocos2dx
#include "math/Vec2.h"
#include "math/CCGeometry.h"
// Utilities
#include "CommonVariables.h"
// Local
#include "GameDefinitions.h"
#include "QuestionParser.h"// This has the Question struct

// Forward declerations
class QCard;
namespace cocos2d

class Layer;
class Sprite;


class QCardManager


QCardManager.cpp

#include "QCardManager.h"
// Local
#include "QCard.h"
#include "RandomPostureSprite.h"
// Utilities
#include "GameManager.h"
#include "GameSettings.h"
#include "CocosUtils.h"
// Cocos2dx
#include "cocos2d.h"
using namespace cocos2d;

QCardManager::QCardManager(PLAYER player, Layer &parent)

    // This line gives the linker error
    QCard::create(PLAYER::PLAYER_ONE, nullptr, CARD_TYPE::QUESTION, 1);

QCardManager 引发链接器错误。但是TimerHUD 没有。我现在正在分享内容。

TimerHUD.h

// Cocos2dx
#include "2d/CCNode.h"

namespace cocos2d

class Sprite;
class Label;


class TimerHUD : public cocos2d::Node


TimerHUD.cpp

// Cocos2dx
#include "cocos2d.h"
#include "SimpleAudioEngine.h"
// Local
#include "GameDefinitions.h"
// Utilities
#include "GameManager.h"
#include "GameSettings.h"
#include "CocosUtils.h"
#include "QCard.h"
using namespace cocos2d;

TimerHUD::TimerHUD()

    // This does not raise the linker error
    QCard::create(PLAYER::PLAYER_ONE, nullptr, CARD_TYPE::QUESTION, 1);

【问题讨论】:

你说得对,Questionstruct。将前向声明从class Question; 更改为struct Question 后,问题就消失了。但这对我提出了另一个问题,在进行更改之前,引发错误的相同代码并未在另一个类中引发错误。我在问题的编辑中添加了更多解释。谢谢。 【参考方案1】:

您不应该需要Question 的定义才能正确链接。 @PBUQuestion@ 中的 U 和链接器错误似乎在谈论 struct Question 而不是 class Question,因此您在 Question 的声明和定义之间存在不匹配。如果您要提高警告级别,您会看到:

> type a.cpp
struct A;
class A ;

> cl /Wall a.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24213.1 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

a.cpp
a.cpp(2): warning C4099: 'A': type name first seen using 'struct' now seen using 'class'
a.cpp(2): note: see declaration of 'A'

但由于您的警告级别太低,您无法获得该诊断。

您的代码似乎在standard's point of view 中有效。从 C++11 9.1/2 开始:

声明仅由类键标识符组成;要么是当前范围内名称的重新声明,要么是标识符作为类名的前向声明。它将类名引入当前作用域。

但是,Visual C++ 会根据名称是 class 还是 struct,对函数名称进行不同的处理:

> undname ?f@@YAXPAUA@@@Z
void __cdecl f(struct A *)

> undname ?f@@YAXPAVA@@@Z
void __cdecl f(class A *)

请注意,struct 被修改为 Uclass 被修改为 V。这是definitely a bug in Visual C++,不应区别对待classstruct

一旦理解了这一点,您的错误就很容易重现:

> type a.cpp
class A;          // declares A as a class
void f(A* a);     // declares f(class A*)

int main()

    f(nullptr);   // calls f(class A*)



> type b.cpp
struct A ;      // defines A as a struct, mismatch!
void f(A* a)    // defines f(struct A*)!


> cl /nologo a.cpp b.cpp
a.cpp
b.cpp
Generating Code...
a.obj : error LNK2019: unresolved external symbol
    "void __cdecl f(class A *)" (?f@@YAXPAVA@@@Z) referenced
        in function _main
a.exe : fatal error LNK1120: 1 unresolved externals

因此,您遇到这些奇怪问题的原因是因为行为取决于特定的translation unit(基本上是.cpp)是否将Question 视为classstruct。这又取决于包含哪些标头。

请注意,您需要将不匹配项放在不同的翻译单元中。在同一个单元内Visual C++ will use the the class-key from the definition,即使之前有不同的声明:

编译器警告(2 级)C4099

'identifier' : 第一次看到使用 'objecttype1' 现在看到使用 'objecttype2' 的类型名称

声明为结构的对象定义为类,或声明为类的对象定义为结构。编译器使用定义中给出的类型。

至于在 Visual C++ 2013 中这不是问题的原因,我怀疑修改方案最近发生了变化。听起来这个错误已经存在since at least 6.0。您可能无意中更改了包含的顺序。

【讨论】:

以上是关于LNK2001 在 VS 2013 (MSVC 18) 上,但不在 VS 2015 (MSVC 19) 上的主要内容,如果未能解决你的问题,请参考以下文章

Qt 5.5.1 MSVC 2013,32 位 - LNK1181:无法打开输入文件 'NIDAQmx.lib'

dcmtk lnk2019 无法解析外部符号

VS2013编译报错——error LNK2001: 无法解析的外部符号 __imp_PathMatchSpecAE:CaffeProgram3train_mnist(p)3train_mnis

log4cplus 无法解析的外部符号 LNK2001

error LNK2001: 无法解析的外部符号

error LNK2001: 无法解析的外部符号