今天同事遇到一个编译错误找我帮忙看一下。原以为编译错误应该好解决,没想到搞了半天才fix,在此记录一下。
由于公司代码涉及机密且比较复杂,不方便贴出来,因此我写了一个简化的例子来展示这个编译错误是如何产生的。

1.代码

假设我们有一个namespace名为zoo, 在zoo中有一个animal基类,一个name类,还有一个dog类继承自animal类。这三个类分别位于三个头文件。代码如下:

//animal.h
#pragma once
class Name;
namespace zoo {class Animal {virtual Name* GetName() const = 0;};
}//name.h
#pragma once
namespace zoo {class Name {};
}//dog.h
#pragma once
#include "animal.h"
#include "name.h"
namespace zoo {class Dog :public  Animal{public:virtual Name* GetName() const override { return name; }private: Name* name;};
}
// main.cpp
#include "dog.h"
int main()
{return 0;
}

2. 原因分析

这段代码很简单,不需要解释。用msvc编译器来编译会得到下面的编译错误:

Error    C2555    'zoo::Dog::GetName': overriding virtual function return type differs and is not covariant from 'zoo::Animal::GetName'    Project2    C:\Users\vincent.zheng\source\repos\Project2\Project2\dog.h    7

第一次看这个报错信息真是看的我一脸懵,一般来讲,C2555报错是由于派生类重写基类的虚函数,但是返回类型不一致导致的,根据微软的官方文档

'class1::function1': overriding virtual function return type differs and is not covariant from 'class2::function2'A virtual function and a derived overriding function have identical parameter lists but different return types.

可是在我们这个例子中,基类和派生类的GetName()函数返回值明明是一样的,都是Name类型。这是一个实实在在的类型,根本不存在covariant的问题。那么问题到底出在哪里呢?

一开始我认为编译器报错信息不准确,就忽略了这个报错,在代码别的地方一通瞎改。可想而知,这样搞肯定是不管用的,搞了一个多小时,这个报错始终跟个牛皮癣一样英魂不散。后来实在累了,于是起来活动一下,喝杯水,水水群再重新坐下来仔细看代码。

终于让我给看出点端倪来,我发现在animal.h文件中,用了一个class前置声明,是这样写的(当然具体的项目代码分散在各个文件,而且位置距离很远,没有这么简单):

class Name;
namespace zoo {class Animal {virtual Name* GetName() const = 0;};
}

这简直是个大坑!class Name的前置声明放在了namespace zoo外面,也就是说编译在编译基类的GetName函数时会认为这里Name是一个全局的class,而不是在namespace zoo里面定义的那个Name。而在文件dog.h中包含了name.h头文件,因此dog类的getName函数返回值是zoo::Name类型。基类和派生类的函数签名分别为

Name* zoo::Animal::GetName()const;
zoo::Name zoo::Dog::GetName() const;

显然这两者的返回值是不一样的,因此会报C2555错误:

Error    C2555    'zoo::Dog::GetName': overriding virtual function return type differs and is not covariant from 'zoo::Animal::GetName'    Project2    C:\Users\vincent.zheng\source\repos\Project2\Project2\dog.h    7

3. 解决方法

知道了原因之后,解决办法自然而然也就有了。要么我们animal.h中的前置声明写在namespace zoo里面,如:

#pragma once
namespace zoo {
class Name;class Animal {virtual Name* GetName() const = 0;};
}

要么,我们不用前置声明,直接include 头文件

#pragma once
#include "Name.h"
namespace zoo {class Animal {virtual Name* GetName() const = 0;};
}

4. 总结

通过这个例子来看编译器的报错还是相当靠谱的,可惜的是这个报错信息只把函数名打印出来了,并没有打印函数签名。如果把函数签名也打印出来,我想这个错误应该分分钟就可以被解决掉。对比下g++编译器的报错信息,明显比MSVC提供的信息要多一些。

In file included from main.cpp:1:
./dog.h:7:17: error: return type of virtual function 'GetName' is not covariant with the return type of the function it overrides ('zoo::Name *' is not derived from 'Name *')virtual Name* GetName() const override { return name; }~~~~~ ^
./animal.h:5:17: note: overridden virtual function is herevirtual Name* GetName() const = 0;~~~~~ ^
1 error generated.

当然,说来说去还是自己经验不足,技术差不能怪编译器:)。这个问题也给了我一个教训:不要轻易放过编译器的报错,大多数情况下编译器还是比我们自己靠谱的多!

记一个bug : C2555 overriding virtual function return type differs and is not covariant from相关推荐

  1. 记一个bug定位与修复过程

    flash程序,在前台修改图片之后,提供打印和Email功能.这两个功能都是把图片发到服务器上,然后跳转到Email页面和打印页面.Email页面多做一个动作,就是通过SMTP把嵌入图片的Html发出 ...

  2. C++中的虚函数(virtual function)

    1回顶部 一.简介 虚函数是C++中用于实现多态(polymorphism)的机制.核心理念就是通过基类访问派生类定义的函数.假设我们有下面的类层次: class A { public: virtua ...

  3. 虚函数(Virtual Function)与 纯虚函数(Pure Virtual Function)

    1>虚函数(Virtual Function) 1.1>Base Class #ifndef Animal_h #define Animal_h #include <string&g ...

  4. php is_subclass_of,PHP_PHP is_subclass_of函数的一个BUG和解决方法,is_subclass_of的作用: 复制代码 - phpStudy...

    PHP is_subclass_of函数的一个BUG和解决方法 is_subclass_of的作用: bool is_subclass_of ( object object, string class ...

  5. php is_subclass_of,PHP is_subclass_of函数的一个BUG和解决方法

    is_subclass_of的作用: 复制代码 代码如下: bool is_subclass_of ( object object, string class_name ) 如果对象 object 所 ...

  6. 从一个 bug 中延伸出 canvas 最大内存限制和浏览器渲染原理

    本文作者:雨过天晴 https://zhuanlan.zhihu.com/p/540761999 前几天一个同事让我帮忙解决一个 bug,这个 bug 困扰他好几天了.这是一个 App 中的 Hybr ...

  7. R6025 - pure virtual function call

    今天的一个程序出了 R6025 - pure virtual function call 错误,主要原因是在基类的构造函数中调用了纯虚函数. 1. 如果不是纯虚函数,没问题. 2. 如果构造函数直接调 ...

  8. 纯虚类(virtual class)和纯虚方法(pure virtual function)

    具体内容参加绿皮书, virtual method class Transaction;rand bit[31:0] src,dst,data[8];bit[31:0]crc;virtual func ...

  9. 【总结】1519- 从一个 bug 中延伸出 canvas 最大内存限制和浏览器渲染原理

    本文作者:雨过天晴 https://zhuanlan.zhihu.com/p/540761999 前几天一个同事让我帮忙解决一个 bug,这个 bug 困扰他好几天了.这是一个 App 中的 Hybr ...

最新文章

  1. 许昌科技学校工业机器人_【调研】省人大常委会副主任徐济超到许昌科技学校进行专题调研...
  2. R语言使用比例-位置图(Scale-Location Plot)和Breusch-Pagan检验验证回归模型残差的同方差性(方差齐性)
  3. 问题征集 | 跟计算机史学家对谈是一种怎样的感受
  4. STM32H7的Cache和MPU
  5. Intellij IDEA2019项目包分层结构显示设置
  6. 云原生下日志方案的架构设计
  7. jQuery: jscolor - color picker
  8. python设计模式21-策略模式
  9. Gitee 上线多项 PR 功能优化,进一步提升审查与提交效率
  10. java中如何引用非静态变量_java为什么不能从静态环境引用非静态变量?
  11. win10屏蔽自动更新方法
  12. Linux-IPC进程间通信(day11)
  13. 3. wordpress 固定链接
  14. (转)洪小文:以科学的方式赤裸裸地剖析AI|混沌初开
  15. unity3d shader 自定义光照函数
  16. C# string转Intptr Intptr转string
  17. WannaCry勒索病毒分析
  18. echart 三维可视化地图_ECharts实现三维可视化
  19. 小失误点,不积跬步无以至千里
  20. 遥控器,未来世界的入口?

热门文章

  1. 公众号如何快速涨粉?教你公众号截流技术快速获取粉丝
  2. 将多个png或者jpg图片转为dicom或者nii格式
  3. Python数据类型,字符编码,文件处理
  4. 关于Windows Build版本号
  5. stm32写字机器人资料包含程序,原理图
  6. 2021,一家民企的“奔富”答卷
  7. 变量、运算符、表达式、输入与输出
  8. LATEX:解决包生成时ins文件无法cannot find的方法
  9. 户外蓝牙耳机什么牌子好、户外运动耳机推荐
  10. 二级域名自动转到子目录下