views:

126

answers:

3

Hi guys, I've run into the following annoying and seemingly incorrect behaviour in the Visual Studio 2008 C++ compiler:

Suppose I have a class library - Car.lib - that uses a "Car" class, with a header called "Car.h":

class Car
{
    public:
    void Drive()
    {
        Accelerate();
    }

    void Accelerate();
};

What I'm actually trying to do is use the Car headers (for some other functions), but without having to link with Car.lib itself (the actual class is not called "Car" but I am sanitising this example).

If I #include "Car.h" in the .cpp file used to build a managed C++ .dll, but never refer to Car, everything compiles and links fine. This is because I never instantiate a Car object.

However, the following:

namespace {
    class Car
    {
    public:
         Car(const Car& rhs)
         {
              Accelerate();
         }

         void Accelerate();
    };
}

leaves me with the link error:

Error 2 error LNK2001: unresolved external symbol "public: void __thiscall `anonymous     namespace'::Car::Accelerate(void)" (?Accelerate@Car@?A0xce3bb5ed@@$$FQAEXXZ) CREObjectWrapper.obj CREObjectBuilderWrapper

Note I've declared the whole thing inside an anonymous namespace so there's no way that the Car functions could be exported from the .DLL in any case.

I can categorically guarantee that Car is not referenced anywhere else, because I just made this class up and typed in the definition from scatch. The "real" class is a different name.

Declaring the copy constructor out-of-line makes no difference. i.e. the following also fails to link:

class Car
{
    public:
    Car(const Car& rhs);
    void Accelerate();
};

Car::Car(const Car& rhs)
{
    Accelerate();
}

It's something specifically to do with the copy constructor note, because the following, for example, does link:

class Car
{
    public:
    Car()
    {
        Accelerate();
    }
    void Accelerate();
};

I am not a C++ standards guru but this doesn't seem correct to me. Surely the compiler still should not have had to even generate any code that calls the Car copy constructor.

Can anyone confirm if this behaviour is correct? It's been a while since I used C++ - but I don't think this used to be an issue with Visual Studio 6.0 for example.

It might be because I am building a managed C++ .dll. (Later: yes, this is exactly the issue. The /clr option appears to introduce this dependency).

Here is the command line being used to build the project:

/OUT:"..\..\bin\Release\CREObjectBuilderWrapper.dll" /INCREMENTAL:NO /NOLOGO /LIBPATH:"..\..\lib\qa\lib" /LIBPATH:"..\..\lib\release" /LIBPATH:"..\..\lib\VDB" /DLL /MANIFEST /MANIFESTFILE:"Release\ObjectBuilderWrapper.dll.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"c:\Release\ObjectBuilderWrapper.pdb" /LTCG /DYNAMICBASE:NO /FIXED:No /MACHINE:X86 /KEYFILE:"c:\src\ObjectBuilderWrapper\\FI.snk" /ERRORREPORT:PROMPT CRERuntime.lib QA.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib

Can anyone suggest a workaround that allows one to "re-use" the Accelerate method from within the copy constructor and still have the copy constructor declared inline?

A: 

Well, it looks like something needs a copy constructor but doesn't need a default constructor. An example of this would be:

std::vector <Car> v;

whether or not you actually populate it.

anon
But *nothing* even refers to this class - *anywhere*. I made up this new class from scratch to reproduce the problem. The original class was called "Value".
Paul Hollingsworth
A: 

Just tried your code with the anonymous namespace with VS2008 and it compiles and links fine.

Make sure you really actually don't refer to Car anywhere.

What happens if you erase the definition of Car completely? does everything still compiles?

shoosh
yes, then everything compiles. As I said in the other comment - I just made up "Car". It's a totally brand new class - so I *know* that nothing is referring to it.
Paul Hollingsworth
Maybe it has something to do with the project being managed..
shoosh
yep I think this must be it... I will see if I can whittle it down to a single managed .dll project that does not link
Paul Hollingsworth
+1  A: 

I have tried to compile your example with VS2008 in a native project; no problem at all. But I have then tried in a managed project and I get the same error. So this is clearly something to do with managed code.

Since C++/CLI is an extension outside the standard, we should not wonder that it does some unexpected things. Not that I can point out the reason why the managed environment needs to run the copy constructor, unfortunately.

Gorpik
yes I have just confirmed this myself.
Paul Hollingsworth