作者LPH66 (かつて交わした约束)
看板C_and_CPP
标题Re: [问题] template ostream
时间Fri Jun 9 05:15:01 2017
※ 引述《moebear (萌熊)》之铭言:
: 开发平台(Platform): (Ex: Win10, Linux, ...)
: win10/linux
: 编译器(Ex: GCC, clang, VC++...)+目标环境(跟开发平台不同的话需列出)
: GCC/VC++
: 额外使用到的函数库(Library Used): (Ex: OpenGL, ...)
: 问题(Question):
: 请问程式码中第6/22/28行,这三个ostream之间的关联性是什麽?
: 25行以上是助教给的程式码,但是我寄信问助教,他只说这是约定俗成的写法 囧。
: 1.为什麽第6行是必备的? 我的理解中,提前宣告是因为实作在後面,中间可能有人用到
: 但是中间到底是谁用到呢? 22行吗? 那为什麽22行会需要用到第6行的宣告呢?
: 2.第22行的<>是什麽意思呢? 我觉得看起来很像是某种template,
: 但是中间又不能塞T进去。
: 3.第28行是我自己写的,我试过很多方法,
: 但是好像只有这样写才可以,跟他关联的好像是第6行,而不是第22行。
: 总之就是这三行之间的关系,以及为什麽22行要这样写?
先讲结论: 根本原因是 CirDequeTemplate 是一个 template class 的关系
如果你只是在写一个非 template class 的 ostream << operator 的话
事情其实很简单:
class IntCirDeque
{
//...
friend ostream& operator << (ostream&, const IntCirDeque&);
};
ostream& operator << (ostream& out, const IntCirDeque& icd)
{
//...
}
这样就行了, 甚至那行 friend 还能充当前置宣告使用
也就是对只引用 IntCirDeque 定义而没有 operator << 实作的地方也能呼叫它
(有个限制就是: 它要能被 ADL 找到才行, 这里显然 ADL 能找得到
不过这跟原问题无关就表过不提)
关於这种写法的细节你应该清楚, 例如为什麽它要是非成员再写 friend, 这里略过
====
那麽为什麽当挂上 template 事情就有点微妙的不一样了?
首先, 先看你的实作 (#27~#28):
template<class T>
std::ostream & operator<< (std::ostream &s, const CirDequeTemplate<T> &cird)
你应该在尝试时注意到了, 因为 cird 是一个 template class
你必须要对每个 T 都要能产生一份这个 operator 才行
如你所观察到的, 跟它相关的是 #6
事实上你这两行扣掉变数名其实是必须要跟 #6 一模一样才行
这正是在达成上面提的「对每个 T 都要能产生一份这个 operator」
这个写法其实就只是一个普通的 template 函数的写法而已
跟 template class 一样, 在前面挂上 template <class T> 然後下面用 T 代换
到这里应该回答了你的问题 3
(一个题外话, 你这里的实作不能用 printf, 因为当 T 不是 int 时就爆了
你应该要 << 进那个 ostream& 里面, 跟 cout 一样的用法只不过 cout 换成这个参数
是说你都在学 C++ 了为什麽还写 printf...)
====
刚才提到「对每个 T 都要能产生一份这个 operator」
所以 class 里面宣告 friend 时必须要宣告
「有跟我一样的 T 的 operator << 是我的朋友」
这里需要提一个你应该也知道的规则:
单独 template 名并不是一个完整的名字, 它一定要有办法知道它的 <> 里是什麽型别
在大多数时候 <> 都不能省略, 否则编译器不会知道这个名字是个 template 名
就算你的 <> 里要用其他资讯推导也是一样
<> 能省略的地方只有你在自己里面指名自己时可以省略
(我知道这里有人要提 template 函数呼叫, 那个最後提)
(这里还必须要真的是指名自己, 也就是 template 参数都要一模一样才行
如果不一样那就还是得乖乖写出来)
这就是这里的 <> 的用途: 表示这个 operator << 是一个 template 名字
那 <> 里面的东西也不必每次都一一指定, 能够推导的就能省略
这个状况里这里面是可以塞 T 的:
friend ostream& operator <<
<T> (ostream&, const CirDequeTemplate<T>&);
即是重覆一次 #6 / #28 的宣告
只不过把宣告用的前置 template <class T> 换成指名用的後置 <T>
但是这个 T 其实是可以从参数推导出来的:
这个宣告的第二参数是一个自己这种物件, 因此这时的 T 是确定的: 跟我自己一样的 T
因此我们可以省略前面的 operator << <T> 里的 T, 就成了 operator << <> 了
後面的 <T> 是属於上面所说指名自己时可省略的状况, 所以也就不写了
这样就成了 #22 的写法:
friend ostream& operator<< <>(ostream&, const CirDequeTemplate&);
以上回答问题 2
====
刚才也提到, #22 的写法里的 operator << 是个 template 名
既然是 template 名那在前面就必须要有对应的 template 宣告才能使用
#6 就正是这个 operator << 所需要的前置 template 宣告
有了 #6 的宣告, #22 的 operator << 才能知道是一个 template 名字
才能跟後续的 template 定义连起来
这里你不能像非 template 版本一样把 template 宣告跟 friend 合起来
原因是你所 friend 的只有这特别的一个 operator << 而已, 不是整个 template
以上回答问题 1
同样的理由 #5 为 #6 里的 CirDequeTemplate 这名字进行前置宣告
====
因此全部总结起来的话就是这样:
#28 为了 template class 所以写成一个单纯的 template 函数
#22 尝试宣告某个特定的 #28 为 friend, 为此需要 #6 前置宣告 #28 的存在
====
最後回头解释一下为什麽上面没提 template 函式呼叫做为省略 <> 的状况
这是因为函式呼叫是个不一样的状况
编译器会取出这个名字, 看看有没有一般函数及 template 函数有这名
如果有看到 template 函数, 会尝试推导要用什麽 template 参数才能符合呼叫方型态
所有能推导出来的版本跟(如果找得到的)一般函数再一起进行 overload resolution
(是的, 我们可以定义同一个名字有非 template 函数和 template 函数)
也就是说, 函数呼叫这个指名并不是直接指名某个 template 函式定义
因此不是属於「提到 template 名」的状况
(然後顺带一提, C++17 把这个推导推广到 constructor 呼叫也适用了
在 C++17 的文件当中叫做 deduction guide, 这里表过不提)
--
Ace Snake Santa Clover Junpei June Seven Lotus 9th man cabin kitchen casino
shower operating room laboratory T H E chart captain quarter confinement
torture room steam engine room cargo chapel library study incinerator Gigantic
Q director office security N O N A R Y archives control laboratory
pec treatment garden pantry gaulem bay rec room crew quarters infirmary lounge
elevator Tenmyouji Quark Dio G A M E S Luna Phi Sigma Alice Clover K
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 180.177.29.238
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1496956504.A.217.html
※ 编辑: LPH66 (180.177.29.238), 06/09/2017 05:15:23
※ 编辑: LPH66 (180.177.29.238), 06/09/2017 05:19:08
1F:→ hunandy14: 呜哇 c++17那个好方便 长知识了 06/09 10:07
2F:推 TianBonBon: 超猛 06/09 11:18
3F:推 moebear: 谢谢! 我懂了 题外话那边是我习惯不好,之後我多加留意 06/09 13:36
4F:推 phishingphi: Effective C++ Item 46 也有类似这里的用法,不过是 06/10 21:50
5F:→ phishingphi: 对 implicit type conversion 的 template 用法。 06/10 21:50
6F:→ phishingphi: 可供有兴趣的人参考。 06/10 21:51
7F:推 xvid: 推 06/15 20:41