google test 打印派生类对象
在Unison中使用google test时,发现EXPECT_EQ在fail时,不能打印Unison Test Language中定义的派生类的对象。于是写了个纯C++的示例,发现在只定义基类的operator<<时,无法打印派生类对象。
于是在github上给googletest提交了一个issueUnable to print derived class while operator<< for base class defined #2435。
示例代码如下:
#include <iostream>
#include <string>
class B {
public:
B(std::string name = "class B") : name_(name) {}
bool operator==(const B& rhs) const { return name_ == rhs.Name(); }
std::string Name()const { return name_;}
private:
std::string name_;
};
class D : public B {
public:
D(std::string name = "class D") : B(name) {}
};
std::ostream& operator<<(std::ostream& os, const B& b) {
os << b.Name();
return os;
}
/* without this operator<< for D, EXPECT_EQ won't print D properly */
/*
std::ostream& operator<<(std::ostream& os, const D& b) {
os << b.Name();
return os;
}
*/
#include <gtest/gtest.h>
TEST(Print, PrintByCout) { std::cout << B() << std::endl << D() << std::endl; }
TEST(Print, PrintB) {
B obj1("b1");
B obj2("b2");
EXPECT_EQ(obj1, obj2);
}
TEST(Print, PrintD) {
D obj1("d1");
D obj2("d2");
EXPECT_EQ(obj1, obj2);
}
执行结果:
Running main() from ./googletest/googletest/src/gtest_main.cc
[==========] Running 3 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 3 tests from Print
[ RUN ] Print.PrintByCout
class B
class D
[ OK ] Print.PrintByCout (0 ms)
[ RUN ] Print.PrintB
sample.cpp:39: Failure
Expected equality of these values:
obj1
Which is: b1
obj2
Which is: b2
[ FAILED ] Print.PrintB (0 ms)
[ RUN ] Print.PrintD
sample.cpp:45: Failure
Expected equality of these values:
obj1
Which is: 32-byte object <90-64 84-4C FF-7F 00-00 02-00 00-00 00-00 00-00 64-31 00-6E 39-56 00-00 E0-E1 75-6E 39-56 00-00>
obj2
Which is: 32-byte object <B0-64 84-4C FF-7F 00-00 02-00 00-00 00-00 00-00 64-32 00-4C FF-7F 00-00 40-7D D9-7F 87-7F 00-00>
[ FAILED ] Print.PrintD (0 ms)
[----------] 3 tests from Print (0 ms total)
[----------] Global test environment tear-down
[==========] 3 tests from 1 test case ran. (0 ms total)
[ PASSED ] 1 test.
[ FAILED ] 2 tests, listed below:
[ FAILED ] Print.PrintB
[ FAILED ] Print.PrintD
没想到很快就有人回复了,kuzkry通过一小段代码说明了原因:
#include <iostream>
struct B {};
struct D : B {};
template <typename T>
void foo(T) {
// This is something that works under the hood of GTest.
// I think we cannot change this as we don't know what types
// users will try to print.
std::cout << "Not what we wanted\n";
}
void foo(const B&) {
// This is what we want.
std::cout << "Good\n";
}
int main() {
foo(D{}); // oops, a function template is a better match
}
并且也还提供了解决办法:
/* template header */
template <typename T, typename = typename std::enable_if<std::is_base_of<B, T>::value>::type> // (C++11)
template <typename T, typename = std::enable_if_t<std::is_base_of<B, T>::value>> // (C++14)
template <typename T, typename = std::enable_if_t<std::is_base_of_v<B, T>>> // (C++17)
std::ostream& operator<<(std::ostream& os, const T& b) {
os << b.Name();
return os;
}
完美!
2019/09/03更新:
这是涉及函数模板的重载问题,《C++ Primer》第五版中有如下说明:
- 对于一个调用,其候选函数包括所有模板实参推断成功的函数模板实例。
- 候选的函数模板总是可行的,因为函数实参推断会排除任何不可行的模板。
- 与往常一样,可行函数(模板和非模板)按类型转换(如果对此调用需要的话)来排序。当然,可以用于函数模板调用的类型转换是非常有限的。
- 与往常一样,如果恰有一个函数提供比任何其他函数都更好的匹配,则选择此函数。但是,如果有多个函数提供同样好的匹配,则:
- 如果同样好的函数中只有一个是非模板函数,则选择此函数。
- 如果同样好的函数中没有非模板函数,而有多个函数模板,且其中一个比其他模板更特例化,则选择此模板。
- 否则,此调用有歧义。
原创文章,始发于https://alancprc.github.io/c++/2019/09/02/google-test-print-derived-class-object.html
原创文章,本文链接https://www.cnblogs.com/alancprc/p/google-test-print-derived-class-object.html

本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
google test 打印派生类对象的更多相关文章
- C++:基类与派生类对象之间的赋值兼容关系
4.5 基类与派生类对象之间的赋值兼容关系 在一定条件下,不同类型的数据之间可以进行类型转换,例如可以将整型数据赋给双精度型变量. 在赋值之前,先把整型数据转换为双精度型数据,然后再把它双精度型变量. ...
- 基类中定义的虚函数在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型及参数的先后顺序,都必须与基类中的原型完全相同 but------> 可以返回派生类对象的引用或指针
您查询的关键词是:c++primer习题15.25 以下是该网页在北京时间 2016年07月15日 02:57:08 的快照: 如果打开速度慢,可以尝试快速版:如果想更新或删除快照,可以投诉快照. ...
- c++,派生类对象可以对基类赋值,基类对派生类不可以赋值
派生类对象可以对基类对象赋值,赋值时属于派生类独有的部分就舍弃不用. #include <iostream> using namespace std; class DemoA { publ ...
- C++程序设计方法3:派生类对象的构造和析构过程
基类中的数据成员,通过继承成为派生类对象的一部分,需要在构造派生类对象的过程中调用基类构造函数来正确初始化: 若没有显示调用,则编译器会自动生成一个对基类的默认构造函数的调用. 若想要显示调用,则只能 ...
- c# 中基类变量指向派生类对象的实例化
这一篇文章转载自:http://www.xuebuyuan.com/390279.html 我对这篇文章进行了一一的验证,确实是这样子的,也明白了很多东西,觉得很有用,转载过来希望能够帮助大家. 1. ...
- C++ 虚函数在基类与派生类对象间的表现及其分析
近来看了侯捷的<深入浅出MFC>,读到C++重要性质中的虚函数与多态那部分内容时,顿时有了疑惑.因为书中说了这么一句:使用“基类之指针”指向“派生类之对象”,由该指针只能调用基类所定义的函 ...
- C++中派生类对象的内存布局
主要从三个方面来讲: 1 单一继承 2 多重继承 3 虚拟继承 1 单一继承 (1)派生类完全拥有基类的内存布局,并保证其完整性. 派生类可以看作是完整的基类的Object再加上派生类自己的Objec ...
- C++派生类中如何初始化基类对象(五段代码)
今天收到盛大的面试,问我一个问题,关于派生类中如何初始化基类对象,我在想派生类对于构造函数不都是先构造基类对象,然后在构造子类对象,但是如果我们在成员初始化列表先初始化派生类的私有成员,在函数内去调用 ...
- 派生类地址比基类地址少4(CDerived对象的起始地址存放的是虚表指针vptr,也就是子类的第一项内容。接下来的是基类的成员变量,接下来再是自身的成员变量)
大家对虚表并不陌生,都知道每个含有虚函数的类对象都有1个虚指针,但是在现实使用中,却总是因为这而调试半天,才发现原来是虚指针惹的祸.我这几天在调试代码时候也中招了,我的问题是这样的,如下图,CTree ...
随机推荐
- 【洛谷】P2568 GCD
前言 耻辱,我这个OI界的耻辱! 题目描述 给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数对(x,y)有多少对.输入格式 一个整数N输出格式答案输入输出样例 输入 4 ...
- 【洛谷】P3177 [HAOI2015]树上染色
懒得复制题面了直接传送门吧 分析 直接求点与点之间的距离感觉不是很好求,所以我们考虑换一个求法. 瞄了一眼题解 距离跟路径上边的长度有关,所以我们直接来看每一条边的贡献吧(这谁想得到啊) 对于每一条边 ...
- CFD计算过程发散诸多原因分析【转载】
转载自: http://blog.sina.com.cn/s/blog_5fdfa7e601010rkx.html 今天探讨引起CFD计算过程中发散的一些原因.cfd计算是将描述物理问题的偏微分方程转 ...
- 深度学习面试题03:改进版梯度下降法Adagrad、RMSprop、Momentum、Adam
目录 Adagrad法 RMSprop法 Momentum法 Adam法 参考资料 发展历史 标准梯度下降法的缺陷 如果学习率选的不恰当会出现以上情况 因此有一些自动调学习率的方法.一般来说,随着迭代 ...
- 【转】利用Python将多个PDF合并为一个
本脚本用来合并pdf文件,输出的pdf文件按输入的pdf文件名生成书签 使用示例如下: python pdfmerge.py -p "D:\pdf-files" -o " ...
- Type-C转接头 还是别用了,影响速率啊
今天用Type-C转接USB头传照片真慢! 在数码配件领域,越是不起眼的小外设,隐藏其背后的猫腻和水分也就越多.就拿常见的Micro USB转USB Type-C转接头和TF转SD卡套而言,你觉得 ...
- sqlserver2016 kb补丁
1. win2012r2 安装时 总是提示: 然后费了半天劲 下载下来又提示 找了一下 需要先安装这么一个补丁才可以 KB2919442 然后才能安装上 KB2919355 然后就可以正常安装了:
- mysql批量更新update中的锁表机制
mysql的行锁是通过索引加载的,即行锁是加在索引响应的行上的,要是对应的SQL语句没有走索引,则会全表扫描,行锁则无法实现,取而代之的是表锁. CREATE TABLE SIMPLE_USER( I ...
- vbs msgbox提示信息最前面显示
msgbox strContent, vbOKOnly or vbExclamation or vbSystemModal,strTitle 提示框类型列表: 常数 值 描述 vbOKOnly 0 只 ...
- python 设计模式之单例模式 Singleton Pattern
#引入 一个类被设计出来,就意味着它具有某种行为(方法),属性(成员变量).一般情况下,当我们想使用这个类时,会使用new 关键字,这时候jvm会帮我们构造一个该类的实例.这么做会比较耗费资源. 如果 ...