14

我收到编译器警告,在这种情况下我不明白。当我从以下代码编译“Child.cpp”时。(不要奇怪:我将我的类声明剥离到最低限度,所以内容没有多大意义,但你会更快地看到问题)。我收到了最高警告级别的Visual Studio 2003Visual Studio 2008警告。


编码

抽象类.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;
}

孩子.h:

#include "AbstractClass.h"

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

孩子.cpp:

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

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

警告

“Child”类派生自“AbstractClass”。在“AbstractClass”中有公共方法“AbstractClass::Cancel()”。如果我在类主体之外定义方法(如您看到的代码中),我会收到编译器警告...

AbstractClass.h(7):警告 C4505:'AbstractClass::Cancel':未引用的本地函数已使用 [T=int] 删除

...当我编译“Child.cpp”时。这个我不明白,因为这是一个公共函数,编译器不知道我以后有没有引用这个方法。最后,我引用了这个方法,因为我在 main.cpp 中调用它,尽管有这个编译器警告,但如果我编译并链接所有文件并执行程序,这个方法仍然有效:

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

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

如果我确实将 Cancel() 函数定义为内联(您将其视为 AbstractClass.h 中的注释代码),那么我不会收到编译器警告。当然我的程序可以工作,但我想了解这个警告还是这只是一个编译器错误?

此外,如果不将 AbsctractClass 实现为模板类(在这种情况下仅用于测试目的),我也不会收到编译器警告...?


如果我创建一个非虚拟函数,我不会收到该非虚拟函数的编译警告,但到目前为止所有答案都不包含虚拟内容。尝试这个:

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;
}

知道的答案对我有帮助,但我认为这个问题没有得到完全回答。

4

7 回答 7

6

我在这里看不到正确的答案:如果模板类中有纯虚方法,Visual Studio会错误地报告此警告。在这种情况下,其他编译器(如 gcc 和 clang)似乎不会报告此警告。

模板化或非模板化类中的纯虚方法是完全合理的,而且通常是一个好主意——将方法声明为纯虚方法会强制您在派生类中实现它。

我没有在任何地方找到对这个错误的引用 - 我不在 Microsoft 开发人员计划中,也许有人可以提交这个错误?

于 2012-01-09T19:30:01.233 回答
3

我不认为警告是故意的。编译器错误地认为该函数是翻译单元的本地函数,但该函数根本不是这样。您在来自 的其他翻译单元中使用生成的函数main,因此使用了该函数。您想出的使警告消失的不同方法似乎只是解决编译器中错误路径的不同方法。

不同之virtual处在于virtual即使不使用函数也可以实例化它们。当他们的类通常被隐式实例化时,就会发生这种情况。该标准声明有效(我强调)

实现不应隐式实例化不需要实例化的类模板的函数模板、成员模板、非虚拟成员函数、成员类或静态数据成员。如果虚拟成员函数不会被实例化,则未指定实现是否隐式实例化类模板的虚拟成员函数。

在这种情况下,同一个虚函数有两个隐式实例化。一个 inChild.h没有任何用处,因此编译器认为该函数是无用的。但是,由于在其他地方(在)中使用了相同的功能main.cpp,因此该警告显然是不一致的。

于 2010-06-25T18:18:57.547 回答
1

警告只是说链接器看不到该函数的任何用法。

如果您想“告诉”链接器避免警告,您可以欺骗链接器“认为”它正在使用中。

例如:

void MyLinkerThinkNotUsedFunction
{
}

void* Foo = (void*)MyLinkerThinkNotUsedFunction;

足以避免功能上的任何警告 C4505 MyLinkerThinkNotUsedFunction

于 2016-01-16T15:38:37.897 回答
0

当编译器遇到该方法的代码时,会编译正常的非模板类方法中的代码。

对于模板类,这是不同的。代码在头文件中,所以如果编译器每次遇到代码时都会编译它,这意味着这个方法会被一遍又一遍地编译,即使你的代码没有调用它。假设 child.h 包含在 1000 个其他文件中。您希望编译器编译 Cancel 方法 1000 次,还是仅在实际调用 Cancel 时编译?

child.cpp 包含 child.h,但不调用 Cancel 方法。因此 Cancel 没有被编译(虽然我觉得奇怪的是你收到了警告)。

main.cpp中还包含child.h,这次调用的是Cancel方法,是编译器编译该方法的信号。最后,链接器将找到 Cancel 方法的所有已编译实例并将它们合并。

于 2010-06-16T09:25:28.317 回答
0

模板在代码生成之前被实例化。这意味着编译器需要知道模板中使用的特定类才能为该模板生成代码。因此,当您在单独的单元中定义模板类方法时,其定义在模板实例化时是未知的。

警告很可能意味着代码AbstractClass<T>::Cancel不是以您用于AbstractClass<T>::Cancel定义的单位生成的。模板类方法仅在使用时生成(即引用、调用),而普通方法代码则在遇到时生成。

如果您尝试AbstractClass<T>::Cancel从 ieAbstractClass.cppCancel定义的函数调用,则警告应该消失。

于 2010-06-16T09:33:03.723 回答
0

在 Visual Studio 2010 中,如果您为派生类(您的子类)定义构造函数,此警告将消失。

于 2015-06-05T07:53:32.167 回答
-2

将函数模板设为虚拟是不合法的。请参阅 Stack Overflow 问题 使函数模板专业化虚拟合法吗?.

于 2010-06-16T09:29:35.773 回答