Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Visual C++ 2015 linker error when forward declaring a struct as class

Tags:

c++

visual-c++

I have the following code (more than one file involved)...

//--- 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>()};
}

Using the Visual C++ compiler, I get a linker error (LNK2019). Using GCC 4.8.4 this is not the case. Changing the forward declaration class SomeInterface to struct SomeInterface makes the linker error go away. I always thought that one should be able to use class/struct interchangeably? The interface of SomeInterfaceUser should not depend on whether SomeInterface is defined as class or struct, not so?

Is this a Visual C++ bug. I cannot find anything relating to it. I suspect the fact that the struct is used as template parameter has something to do with it.

Your help appreciated.

like image 670
Werner Erasmus Avatar asked Nov 02 '15 17:11

Werner Erasmus


People also ask

How to solve linker error in visual studio?

In Visual Studio, make sure the source file that defines the symbol gets compiled as part of your project. Check the intermediate build output directory for a matching . obj file. If the source file isn't compiled, right-click on the file in Solution Explorer and choose Properties to check the properties of the file.

Can you forward declare a struct?

In C++, classes and structs can be forward-declared like this: class MyClass; struct MyStruct; In C++, classes can be forward-declared if you only need to use the pointer-to-that-class type (since all object pointers are the same size, and this is what the compiler cares about).

What is an unresolved external symbol in C++?

The unresolved external symbol is a linker error that indicates it cannot find the symbol or its reference during the linking process. The error is similar to “undefined reference” and is issued interchangeably.


1 Answers

I've just been facing the same problem with both VC++ 2010 and VC++ 2017, and after some tests I've found that the problem resides in the symbol name the compiler gives to structs and classes internally.

Here a minimum example consisting in three files:

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

When the project is compiled the following errors and warnings appear:

warning C4099: 'Foo': type name first seen using 'class' now seen using 'struct'

see declaration of 'Foo'

error LNK2019: unresolved external symbol "void __cdecl bar(struct Foo *)" (?bar@@YAXPAUFoo@@@Z) referenced in function _main

fatal error LNK1120: 1 unresolved externals

Now, I swapped the struct and class declarations, so main.cpp and bar.h are now:

main.cpp

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

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

bar.h

struct Foo;
void bar(Foo* f);

As expected, the error still pops up:

error LNK2019: unresolved external symbol "void __cdecl bar(class Foo *)" (?bar@@YAXPAVFoo@@@Z) referenced in function _main

BUT, and this is the interesting part, see that the symbols for the expected function (the one used in main()) in each case differ:

  • ?bar@@YAXPAUFoo@@@Z (when the parameter is a struct)

  • ?bar@@YAXPAVFoo@@@Z (when the parameter is a class)


Conclusion

The compiler gives slightly different names if the type is a struct or a class.

The linker then cannot find the proper definition because it is looking for a different one: bar.cpp defines one with the forward declaration, but for the moment it is called in main.cpp the actual declaration has taken placed, so a different function name is given in the symbols table.

like image 119
cbuchart Avatar answered Sep 21 '22 02:09

cbuchart