VS 2013上的LNK2001(MSVC 18)但VS 2015上没有(MSVC 19)

时间:2016-08-17 14:10:49

标签: c++ visual-c++

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

  

QCardManager.cpp.obj:-1:错误:LNK2001:未解析的外部符号   “public:static class QCard * __cdecl QCard :: create(enum PLAYER,struct   问题const *,枚举CARD_TYPE,int const&)“   (?创建@ @@ 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;前向声明。我在QuestionParser.h中添加了QCard.cpp文件,但由于我在QCard中使用QCard.h的前向声明,QCardManager.cpp文件没有{{1}的实现因此链接器错误。

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

编辑1 : 原来声明应该是Question而不是struct Question。当我尝试在class Question中使用QCard::create时,我得到上述链接器错误。但不在QCardManager.cpp中,它位于同一目录中。我将发布他们的摘要内容。请记住,我通过此编辑保持TimerHUD.cpp的声明相同。

问题结构,位于QCard

QuestionParser.h

QCardManager.h

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

QCardManager.cpp

// 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
{
}

#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.h

TimerHUD

TimerHUD.cpp

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

namespace cocos2d
{
class Sprite;
class Label;
}

class TimerHUD : public cocos2d::Node
{
}

1 个答案:

答案 0 :(得分:6)

您无需Question的定义即可正确链接。 U中的@PBUQuestion@和链接器错误似乎与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:

  

声明仅由类 - 密钥标识符组成; 是对当前作用域中名称的重新声明,或者是作为类名称的标识符的前向声明。它将类名引入当前范围。

但是,根据名称是class还是struct,Visual C ++会以不同方式破坏函数的名称:

> 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看作{{1}或class。这又取决于包含哪些标题。

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

  

编译器警告(级别2)C4099

     

'标识符' :首先使用' objecttype1'现在看到使用' objecttype2'

     

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

至于为什么这不是Visual C ++ 2013中的一个问题,我怀疑最近的修改方案是否有所改变。听起来这个bug已经存在since at least 6.0。您可能无意中更改了包含的顺序。