向前声明结构为类时,Visual C ++ 2015链接器错误

时间:2015-11-02 17:40:01

标签: c++ visual-c++

我有以下代码(涉及多个文件)......

//--- SomeInterface.h
struct SomeInterface
{
  virtual void foo() = 0;
  virtual ~SomeInterface(){}
};

//--- SomeInterfaceUser.h
#include <memory> //shared_ptr

class SomeInterface;
//NOTE: struct SomeInterface... causes linker error to go away...

class SomeInterfaceUser
{
  public:
    explicit SomeInterfaceUser(std::shared_ptr<SomeInterface> s);
};

//SomeInterfaceUser.cpp
#include "SomeInterfaceUser.h"
#include "SomeInterface.h"
SomeInterfaceUser::SomeInterfaceUser(std::shared_ptr<SomeInterface> s)
{
}

//SomerInterfaceUserInstantiator.cpp
#include "SomeInterfaceUser.h"
#include "SomeInterfaceImpl.h"

struct SomeInterfaceImpl : SomeInterface
{
  virtual void foo(){}
};

void test()
{
  SomeInterfaceUser x{std::make_shared<SomeInterfaceImpl>()};
}

使用Visual C ++编译器,我收到链接器错误(LNK2019)。使用GCC 4.8.4并非如此。将前向声明类SomeInterface 更改为 struct SomeInterface 会使链接器错误消失。我一直认为应该能够交替使用class / struct? SomeInterfaceUser的接口不应该依赖于SomeInterface是定义为类还是结构,不是这样?

这是一个Visual C ++错误。我找不到与之相关的任何内容。我怀疑结构被用作模板参数的事实与它有关。

您的帮助表示赞赏。

1 个答案:

答案 0 :(得分:4)

我刚刚面临VC ++ 2010和VC ++ 2017的同样问题,经过一些测试后我发现问题存在于编译器内部对结构和类的符号名称中。

这是一个包含三个文件的最小示例:

<强>的main.cpp

#include "bar.h"
struct Foo {};

int main() {
  Foo foo;
  bar(&foo);
  return 0;
}

<强> bar.h

class Foo;
void bar(Foo* f);

<强> bar.cpp

#include "bar.h"

void bar(Foo* foo) {}

编译项目时,会出现以下错误和警告:

  

警告C4099:'Foo':使用'struct'时首次看到使用'class'的类型名称

     

参见'Foo'的声明

     

错误LNK2019:未解析的外部符号“void __cdecl bar(struct Foo *)”(?bar @@ YAXPAUFoo @@@ Z)在函数_main中引用

     

致命错误LNK1120:1个未解析的外部

现在,我换了structclass声明,现在main.cppbar.h

<强>的main.cpp

#include "bar.h"
class Foo {};

int main() {
  Foo foo;
  bar(&foo);
  return 0;
}

<强> bar.h

struct Foo;
void bar(Foo* f);

正如预期的那样,错误仍然会出现:

  

错误LNK2019:未解析的外部符号“void __cdecl bar(class Foo *)”(?bar @@ YAXPAVFoo @@@ Z)在函数_main中引用

但是,这是有趣的部分,请看每种情况下预期函数的符号(main()中使用的符号)不同:

  • ?bar @@ YAXPAUFoo @@@ Z (当参数为struct时)

  • ?bar @@ YAXPAVFoo @@@ Z (当参数为class时)

<强>结论

如果类型是结构或类,编译器会给出稍微不同的名称。

然后链接器找不到正确的定义,因为它正在寻找另一个定义:bar.cpp定义一个带有前向声明的定义,但目前它在main.cpp中被调用,实际声明已经采用放置,因此符号表中给出了不同的函数名称。