14
votes

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 minimum, so the content will not make much sense, but you will see the problem quicker). I get the warning with Visual Studio 2003 and Visual Studio 2008 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...?


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.

7
MSDN sort of explains what is also explained in a few posts below: msdn.microsoft.com/en-us/library/z85eyax0.aspxjilles de wit
But the warning explanation did not help me in this case. The meaning of the warning would have been clear to me in any other case, but here my trouble was caused by the template stuff. Thanks anyway for your effort.eike
What happens if you mark Cancel() as inline while keeping it outside the class definition?sbk
@sbk: If I mark it as inline, but keep the code definition outside, I don't get the warning.eike
Even if you don't use template, but you don't use inline methods, I think you may get some errors if you use the AbstractClass.h in other files besides Child.h. For example, there is another file AnotherChild.h that include AbstractClass.h, which is also included in the main.cpp.leiiv

7 Answers

6
votes

I don't see the correct answer anywhere here: if you have a pure virtual method in a templated class, Visual Studio incorrectly reports this warning. Other compilers, like gcc and clang, do not appear to report this warning in this case.

Pure virtual methods in templated or non-templated classes are perfectly reasonable, and often a good idea - declaring the method as pure virtual forces you to implement it in derived classes.

I didn't find a reference to this anywhere as a bug - I'm not in a Microsoft developer program, perhaps someone who is could file this bug?

3
votes

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.

1
votes

The warning simply says that the linker cannot see any usage of the function.

If you want to"tell" the linker to avoid the warning you can fool the linker into "think" it is in use.

For example:

void MyLinkerThinkNotUsedFunction
{
}

void* Foo = (void*)MyLinkerThinkNotUsedFunction;

will suffice to avoid any warning C4505 on the function MyLinkerThinkNotUsedFunction.

0
votes

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.

0
votes

In Visual Studio 2010 this warning disappears if you define a constructor for the derived class (your child class).

0
votes

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, its definition is unknown at the time of template instantiation.

The warning most likely means that the code for AbstractClass<T>::Cancel is not generated in the unit you used for AbstractClass<T>::Cancel definition. Template class methods are only generated once they're used (i.e. referenced, called), as opposed to normal method code, which is generated once it's encountered.

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

-2
votes

Making a function template virtual is not legal. See Stack Overflow question Is making a function template specialization virtual legal?.