繁体   English   中英

C ++模板基类的编译器警告

[英]Compiler warning at C++ template base class

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


编码

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

警告

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

知道的答案对我有所帮助,但我不认为这个问题得到了充分的回答。

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

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

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

我认为警告不是故意的。 编译器错误地认为该函数是翻译单元的本地函数,但该函数根本不是这样。 可以使用所生成的功能在其他翻译单元从main ,从而使用该函数。 您想出的使警告消失的不同方式似乎是在编译器中解决错误路径的不同方法。

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

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

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

当编译器遇到方法的代码时,将编译普通的非模板化类方法中的代码。

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

child.cpp包含child.h,但不调用Cancel方法。 因此,不会编译取消(虽然我觉得很奇怪你会收到警告)。

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

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

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

如果你试图调用AbstractClass<T>::Cancel从即功能AbstractClass.cpp ,其中Cancel定义,警告就会消失。

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

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

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

例如:

void MyLinkerThinkNotUsedFunction
{
}

void* Foo = (void*)MyLinkerThinkNotUsedFunction;

将足以避免在函数MyLinkerThinkNotUsedFunction上发出任何警告MyLinkerThinkNotUsedFunction

使功能模板虚拟是不合法的。 请参阅Stack Overflow问题正在使功能模板专业化虚拟合法吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM