﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Error-随笔分类-为什么用boost</title><link>http://www.cppblog.com/Error/category/19521.html</link><description /><language>zh-cn</language><lastBuildDate>Sat, 23 Jun 2012 13:47:11 GMT</lastBuildDate><pubDate>Sat, 23 Jun 2012 13:47:11 GMT</pubDate><ttl>60</ttl><item><title>(转)boost::implicit_cast</title><link>http://www.cppblog.com/Error/archive/2012/06/23/179905.html</link><dc:creator>Enic</dc:creator><author>Enic</author><pubDate>Sat, 23 Jun 2012 13:34:00 GMT</pubDate><guid>http://www.cppblog.com/Error/archive/2012/06/23/179905.html</guid><wfw:comment>http://www.cppblog.com/Error/comments/179905.html</wfw:comment><comments>http://www.cppblog.com/Error/archive/2012/06/23/179905.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Error/comments/commentRss/179905.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Error/services/trackbacks/179905.html</trackback:ping><description><![CDATA[<h4>boost::implicit_cast</h4> <p>在stackoverflow上看到<a href="http://stackoverflow.com/questions/868306/what-is-the-difference-between-static-cast-and-implicit-cast">这个帖子</a>, 于是发现了boost::implicit_cast这个小东西. </p><p>先来看看这段代码:</p><pre><code>struct top {};
struct mid_a : top {};
struct mid_b : top {};
struct bottom : mid_a, mid_b {};

void foo(mid_a&amp;) {}
void foo(mid_b&amp;) {}
void bar(bottom &amp;arg) {
    foo(arg); // 想要调用"void foo(mid_a&amp;)"
}

int main() {
    bottom x;
    bar(x);
    return 0;
}</code></pre>
<p>是无法编译通过的, 因为foo的重载解析有歧义. 那么把bar里的代码改一改, 为了保持C++风格, 我们使用static_cast, 而不是C风格的转换:</p><pre><code>foo(static_cast&lt;mid_a&amp;&gt;(arg));</code></pre>
<p>程序编译通过了, 运行起来也没有问题, 然而&#8230;
</p><p>一个月以后我把bar的参数类型修改了一下:</p><pre><code>struct top {};
struct mid_a : top {};
struct mid_b : top {};
struct bottom : mid_a, mid_b {};

void foo(mid_a&amp;) {}
void foo(mid_b&amp;) {}
void bar(top &amp;arg) {
    // ... 过了一个月, 这里已经添加了很多代码.
    foo(static_cast&lt;mid_a&amp;&gt;(arg));
}

int main() {
    top x;
    bar(x);
    return 0;
}</code></pre>
<p>代码依旧编译通过, 可是运行时程序挂掉了(假设这几个类里面有许多成员, 并且在foo里对其进行了访问).
</p><p>发现问题了吗? 原因就在于static_cast太强大了, 强大到可以进行&#8221;down-cast&#8221;. 于是编译器没有给你任何警告, 就把一个top类型的引用给强制转换成了min_a的引用.
</p><p>这个时候轮到boost::implicit_cast出场了. 把bar里面的那句foo调用改一改:</p><pre><code>foo(boost::implicit_cast&lt;mid_a&amp;&gt;(arg));</code></pre>
<p>于是一个月前的代码依旧可以通过编译, 而一个月后的代码中的错误被编译器揪出来了. 原因在于隐式类型转换不允许&#8221;down-cast&#8221;, 只能&#8221;up-cast&#8221;.
</p><p>这里简要说一下所谓显式和隐式类型转换的区别. 在C++世界的英文里, 我们说&#8221;convert&#8221;通常指&#8221;implicit convert&#8221;, 而&#8221;cast&#8221;指&#8221;explicit cast&#8221;. 隐式类型转换好理解, 就是你写了个a=b, 而ab不同类型, 编译又不报错, 就说明隐式类型转换发生了, 类似的情况还有在函数调用的参数传递时. 而显式类型转换特指C风格的强制转换((type)obj或者C++中等价的type(obj)), 以及C++风格的四个关键字(static_cast, const_cast, dynamic_cast, reinterpret_cast). 然而这个定义是相当模糊的, 比如一个int类型的x, bool(x)是显式的, 而!!x是隐式的, 其实效果上并没有区别, 只是字面上的不同罢了. (关于cast和convert的区别, 参见<a href="http://stackoverflow.com/questions/1374325/is-there-any-difference-between-type-casting-type-conversion">这里</a>和<a href="http://stackoverflow.com/questions/4344402/c-and-c-difference-between-casting-and-conversion">这里</a>)
</p><p>所以在bar里我们需要的仅仅是一个隐式类型转换, 然而直接把arg传递给foo的话会出现重载歧义, 于是我们需要告诉编译器到底要进行哪个隐式类型转换. 然而static_cast又太过强大, 它还能做隐式类型转换之外的事情(up-cast), 于是在日后代码演化的过程中留下了bug.
</p><p>于是boost::implicit_cast应运而生, 它比static_cast弱, 正如它的名字一样, 它只能用来告诉编译器执行什么隐式类型转换.
</p><p>而它的代码呢? 简单到令人发指:</p><pre><code>template &lt;typename T&gt;
inline T implicit_cast (typename mpl::identity&lt;T&gt;::type x) {
    return x;
}</code></pre>
<p>而mpl::identity的定义也极其简单:</p><pre><code>template&lt;typename T&gt; struct identity { typedef T type; };</code></pre>
<p>有人要问这个identity干什么用的, 看起来很累赘. 如果没有这个identity, 像&#8221;implicit_cast(obj)&#8221;这样的代码也能通过编译, 然而它其实什么也没做, obj的类型仍然没变. identity的存在使得函数模板的参数类型推导失效, 因为要推导出T, 首先得知道identity是什么, 而identity又是依赖于T的. 于是就形成了循环依赖, 参数类型推导就失效了. 于是编译器就要求你显式地指定T的类型.</p><img src ="http://www.cppblog.com/Error/aggbug/179905.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Error/" target="_blank">Enic</a> 2012-06-23 21:34 <a href="http://www.cppblog.com/Error/archive/2012/06/23/179905.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>