发现《深入理解C++11》中扩展的friend代码的错误
目前在总结现代C++的新特性,看了《深入理解C++11》这本书。
今天看到扩展的friend语法这一节,遇到了问题。本节电子版内容参见:https://book.2cto.com/201306/25354.html
未改良前的代码如代码清单2-21所示(在Linux下按照下面的编译指令能够通过),接着作者总结出了这种方法4点不好,给出了改良版代码(如代码清单2-22所示)。我要吐槽的是,反复试了好久,这改良版根本就不能够通过编译,感觉代码写的相当的鬼畜。
代码清单2-21
#include<iostream>
using namespace std;
// 为了方便测试,进行了危险的定义
#ifdef UNIT_TEST
#define private public
#endif
class Defender {
public:
void Defence(int x, int y){}
void Tackle(int x, int y){}
private:
int pos_x = 15;
int pos_y = 0;
int speed = 2;
int stamina = 120;
};
class Attacker {
public:
void Move(int x, int y){}
void SpeedUp(float ratio){}
private:
int pos_x = 0;
int pos_y = -30;
int speed = 3;
int stamina = 100;
};
#ifdef UNIT_TEST
class Validator {
public:
void Validate(int x, int y, Defender & d){}
void Validate(int x, int y, Attacker & a){}
};
int main() {
Defender d;
Attacker a;
a.Move(15, 30);
d.Defence(15, 30);
a.SpeedUp(1.5f);
d.Defence(15, 30);
Validator v;
v.Validate(7, 0, d);
v.Validate(1, -10, a);
return 0;
}
#endif
// 编译选项:g++ 2-9-3.cpp -std=c++11 -DUNIT_TEST
代码清单2-22
#include<iostream>
using namespace std;
template <typename T> class DefenderT {
public:
friend T;
void Defence(int x, int y){}
void Tackle(int x, int y){}
private:
int pos_x = 15;
int pos_y = 0;
int speed = 2;
int stamina = 120;
};
template <typename T> class AttackerT {
public:
friend T;
void Move(int x, int y){}
void SpeedUp(float ratio){}
private:
int pos_x = 0;
int pos_y = -30;
int speed = 3;
int stamina = 100;
};
using Defender = DefenderT<int>; // 普通的类定义,使用int做参数
using Attacker = AttackerT<int>;
#ifdef UNIT_TEST
class Validator {
public:
void Validate(int x, int y, DefenderTest & d){}
// 该行和下一行均报错:语法错误
void Validate(int x, int y, AttackerTest & a){}
//我在想这里的AttackerTest 是下面两行定义的,尝试把下面的两行提到前面错误变得更多了
};
using DefenderTest = DefenderT<Validator>; // 测试专用的定义,Validator类成为友元
using AttackerTest = AttackerT<Validator>;
int main() {
DefenderTest d;
AttackerTest a;
a.Move(15, 30);
d.Defence(15, 30);
a.SpeedUp(1.5f);
d.Defence(15, 30);
Validator v;
v.Validate(7, 0, d) ;
//此处和下一行均报错:函数不接受3个参数
v.Validate(1, -10, a);
return 0;
}
#endif
// 编译选项:g++ 2-9-4.cpp -std=c++11 -DUNIT_TEST
最后修改方法
using DefenderTest = DefenderT<Validator>; // 测试专用的定义,Validator类成为友元
using AttackerTest = AttackerT<Validator>;
把这两行提到
class Validator 之前,因为他的成员函数用到了这些定义
又因为 using DefenderTest = DefenderT<Validator>; 用到了 Valkidator 所以 要在这句话之前,前置声明一下 class Validator;
发现《深入理解C++11》中扩展的friend代码的错误的更多相关文章
- 理解ios 11中webview的视口
iOS 11在状态栏区域带来了一些新的,也许是不直观的行为,这对使用Apache Cordova或Ionic等工具的开发人员尤为重要.特别是,这种行为变化会影响任何基于Web的应用程序,这些应用程序在 ...
- 从linux0.11中起动部分代码看汇编调用c语言函数
上一篇分析了c语言的函数调用栈情况,知道了c语言的函数调用机制后,我们来看一下,linux0.11中起动部分的代码是如何从汇编跳入c语言函数的.在LINUX 0.11中的head.s文件中会看到如下一 ...
- JDK 11中的ZGC-一种可扩展的低延迟垃圾收集器
# 背景正如我们所知道的在JDK 11中即将迎来ZGC(The Z Garbage Collector),这是一个处于实验阶段的,可扩展的低延迟垃圾回收器.本文整合了外网几篇介绍ZGC的文章和代码. ...
- C++11 中值得关注的几大变化(网摘)
C++11 中值得关注的几大变化(详解) 原文出处:[陈皓 coolshell] 源文章来自前C++标准委员会的 Danny Kalev 的 The Biggest Changes in C++11 ...
- 深入理解Java虚拟机--中
深入理解Java虚拟机--中 第6章 类文件结构 6.2 无关性的基石 无关性的基石:有许多可以运行在各种不同平台上的虚拟机,这些虚拟机都可以载入和执行同一种平台无关的字节码(ByteCode),从而 ...
- 深入理解C++11【5】
[深入理解C++11[5]] 1.原子操作与C++11原子类型 C++98 中的原子操作.mutex.pthread: #include<pthread.h> #include <i ...
- 深入理解C++11【4】
[深入理解C++11[4]] 1.基于范围的 for 循环 C++98 中需要告诉编译器循环体界面范围.如for,或stl 中的for_each: int main() { ] = { , , , , ...
- 深入理解C++11【3】
[深入理解C++11[3]] 1.POD类型 Plain Old Data. Plain 表示 了POD是个普通的类型.C++11将POD划分为两个基本概念的合集: 1)平凡的(trivial) 2) ...
- 深入理解C++11
[深入理解C++11] 1.很多 现实 的 编译器 都 支持 C99 标准 中的__ func__ 预定 义 标识符 功能, 其 基本 功能 就是 返回 所在 函数 的 名字. 编译器 会 隐式 地 ...
随机推荐
- SpringMVC系列之主要组件
一.组件说明 DispatcherServlet:前端控制器,用于请求到达前端控制器,由它调用其他组件处理用户的请求. HandlerMapping:处理器映射器,负责根据用户请求找到Handler( ...
- Android数据存储之SD卡
为了更好的存取应用程序的大文件数据,应用程序需要读. 写SD卡上的文件.SD卡大大扩充手机的存储能力. 操作SD首先要加权限: <!--在SDCard中创建与删除文件权限 --> < ...
- VS2008 SP1 安装卡在 VS90sp1-KB945140-X86-CHS的解决方法
VS2008 SP1 安装卡在 VS90sp1-KB945140-X86-CHS的解决方法 VS2008 SP1 安装卡在 VS90sp1-KB945140-X86-CHS的解决方法 方法一:(不推荐 ...
- 【Spring Boot&&Spring Cloud系列】Spring Boot中使用NoSql数据库Redis
github地址:https://github.com/AndyFlower/Spring-Boot-Learn/tree/master/spring-boot-nosql-redis 一.加入依赖到 ...
- 二叉树的实现(Java语言描述)
实现二叉树 并先序遍历之. package 二叉树的实现; public class BinaryTree<T> { class Node { int value; // 该节点存储的 ...
- Linux(Ubuntu)下如何解压 .zip 文件
安装解压缩软件 使用如下命令安装: sudo apt-get install p7zip-full 7zip,估计会比较熟悉. p7zip,是什么? 听说是 7zip 的 shell 封装. 解压 使 ...
- linux常用命令之scp详解
使用scp的前提: 1.服务端启动了sshd服务 2.是本地和远程两端的系统都必须要有scp这个命令.即openssh-clients软件包 [安装方法] [root@ ~]# yum install ...
- 【CF718E】Matvey's Birthday BFS+动态规划
[CF718E]Matvey's Birthday 题意:给你一个长度为n的,由前8个小写字母组成的字符串s.构建一张n个点的无向图:点i和点j之间有一条长度为1的边当且仅当:|i-j|=1或$s_i ...
- s3接口认证说明
S3 Authorization太绕,太头痛,下面解释说明: XS3 REST API基于HMAC(哈希消息身份验证码)密钥使用自定义HTTP方案进行身份验证.要对请求进行身份验证,您首先需要合并请求 ...
- vue之单表输入绑定
vue的核心:声明式的指令和数据的双向绑定. 那么声明式的指令,已经给大家介绍完了.接下来我们来研究一下什么是数据的双向绑定? 另外,大家一定要知道vue的设计模式:MVVM M是Model的简写,V ...