views:

94

answers:

4

I get a compiler warning, that I don't understand in that context, when I compile the "Child.cpp" from the following code. (Don't wonder: I stripped off my class declarations to the bare minuum, so the content will not make much sense, but you will see the problem quicker). I get the warning with VS2003 and VS2008 on the highest warning level.


The code

AbstractClass.h :

#include <iostream>

template<typename T>
class AbstractClass
{
public:
    virtual void Cancel(); // { std::cout << "Abstract Cancel" << std::endl; };
    virtual void Process() = 0;
};

//outside definition. if I comment out this and take the inline
//definition like above (currently commented out), I don't get
//a compiler warning
template<typename T>
void AbstractClass<T>::Cancel()
{
    std::cout << "Abstract Cancel" << std::endl;
}

Child.h :

#include "AbstractClass.h"

class Child : public AbstractClass<int>
{
public:
    virtual void Process();
};

Child.cpp :

#include "Child.h"
#include <iostream>

void Child::Process()
{
    std::cout << "Process" << std::endl;
}

The warning

The class "Child" is derived from "AbstractClass". In "AbstractClass" there's the public method "AbstractClass::Cancel()". If I define the method outside of the class body (like in the code you see), I get the compiler warning...

AbstractClass.h(7) : warning C4505: 'AbstractClass::Cancel' : unreferenced local function has been removed with [T=int]

...when I compile "Child.cpp". I do not understand this, because this is a public function and the compiler can't know if I later reference this method or not. And, in the end, I reference this method, because I call it in main.cpp and despite this compiler warning, this method works if I compile and link all files and execute the program:

//main.cpp
#include <iostream>
#include "Child.h"

int main()
{
    Child child;
    child.Cancel();  //works, despite the warning
}

If I do define the Cancel() function as inline (you see it as out commented code in AbstractClass.h), then I don't get the compiler warning. Of course my program works, but I want to understand this warning or is this just a compiler mistake?

Furthermore, if do not implement AbsctractClass as a template class (just for a test purpose in this case) I also don't get the compiler warning...?


EDIT/ADDITION:

If I make a non-virtual function, I don't get the compile warning for that non-virtual function, but all answers up to now don't comprise the virtual stuff. Try this:

template<typename T>
class AbstractClass
{
public:
    virtual void Cancel(); // { std::cout << "Abstract Cancel" << std::endl; };
    virtual void Process() = 0;
    void NonVirtualFunction();    
};

//...

template<typename T>
void AbstractClass<T>::NonVirtualFunction()
{
    std::cout << "NonVirtualFunction" << std::endl;
}

The answers up to know helped me, but I don't think that the question is fully answered

A: 

The code in normal, non-templated class methods is compiled when the compiler encounters the code for the method.

For templated classes this is different. The code is in the header so if the compiler would compile it every time it encounters the code, it would mean that this method is compiled over and over again, even if it is not called by your code. Suppose that child.h is include in 1000 other files. Do you want the compiler to compile the Cancel method 1000 times, or only when Cancel is actually called?

child.cpp includes child.h, but does not call the Cancel method. Therefore Cancel is not compiled (although I find it strange that you get a warning for this).

main.cpp also includes child.h, and this time it calls the Cancel method, which is a signal for the compiler to compile the method. Finally, the linker will find all compiled instantiations of the Cancel method and merge them.

Patrick
Thanks for your explanation. It's really strange that I get a warning in this case and of course the warning text is actually misleading. However, I just edited my original entry and added the behaviour of a non-virtual function where I don't get a warning. With this behaviour, the compiler warning looks even more like a strange compiler behaviour. But maybe you have a good explanation, too...? Thx for your effort so far!
eike
OK, I now understand why the warning. If the method is not virtual, then you are sure that it's not needed if it is not being called.If it is virtual, then it might still be called by a user of the parent class, and optimizing it away might cause an incomplete vtable. However, I don't know if there is a way to 'force' the code-generation (instead of giving a warning).
Patrick
A: 

Making a function template virtual is not legal http://stackoverflow.com/questions/757270/is-making-a-function-template-specialization-virtual-legal

Pardeep
But the virtual functions itself are not function templates. I have a class template which contains 'normal' virtual functions.
eike
A: 

Templates are instantiated before code generation. This means that compiler needs to know the specific class used in a template to be able to generate code for that template. So, when you define your template class method in a separate unit, it's definition is unknown at the time of template instantiation.

The warning most likely means, that the code for AbstractClass::Cancel is not generated in the unit you used for AbstractClass::Cancel definition.

This basically means that template class methods are only generated once they're used (i.e. referenced, called), as opposed to normal method's code is generated once it's encountered.

If you try to call AbstractClass::Cancel from a function in i.e. AbstractClass.cpp, where Cancel is defined, the warning should go away.

deemoowoor
+1  A: 

I don't think that warning is intentional. The compiler mistakenly belives that the function is local to the translation unit, but the function isn't such at all. You use the generated function in the other translation unit from main, thus the function is used. The different ways you figured out to make the warning disappear just seem to be different ways to work around the buggy path in the compiler.

The difference with regard to virtual is that virtual functions can be instantiated even without a use of them. This happens when their class was implicitly instantiated usually. The Standard declares that valid (emphasis by me)

An implementation shall not implicitly instantiate a function template, a member template, a non-virtual member function, a member class or a static data member of a class template that does not require instantiation. It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated.

In this case there are two implicit instantiations of that same virtual function. The one in Child.h was done without any use, and thus the compiler thinks the function is useless. But as that same function is used elsewhere (in main.cpp), that warning is clearly at odds.

Johannes Schaub - litb