<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.2">Jekyll</generator><link href="https://info.sast.fun/feed.xml" rel="self" type="application/atom+xml" /><link href="https://info.sast.fun/" rel="alternate" type="text/html" /><updated>2023-01-02T03:45:15+00:00</updated><id>https://info.sast.fun/feed.xml</id><title type="html">NJUPT SAST</title><subtitle>南京邮电大学大学生科学技术协会官方网站</subtitle><entry><title type="html">C语言急救车</title><link href="https://info.sast.fun/event/2021/c-final-exam" rel="alternate" type="text/html" title="C语言急救车" /><published>2021-12-24T00:00:00+00:00</published><updated>2021-12-24T00:00:00+00:00</updated><id>https://info.sast.fun/event/2021/C-Final-Exam</id><content type="html" xml:base="https://info.sast.fun/event/2021/c-final-exam">&lt;p&gt;PPT下载：https://www.aliyundrive.com/s/AvVpC1tMbgW&lt;/p&gt;

&lt;p&gt;用一节公开课的时间带领大家将考试的常见知识点和考察方式复习一遍，做到听进一道题就能多得一分，快速高效复习。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;本次公开课无需报名 需自带纸笔&lt;/p&gt;

  &lt;p&gt;不预留座位 前排座位先到先得&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;活动信息：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;主讲人： 黄霄&lt;/li&gt;
  &lt;li&gt;时间： 2021/12/24 18:30 UTC +08:00&lt;/li&gt;
  &lt;li&gt;活动形式：教四-202 线下授课&lt;/li&gt;
  &lt;li&gt;同步直播地址：http://live.bilibili.com/14980641&lt;/li&gt;
&lt;/ul&gt;</content><author><name>matrix72</name></author><category term="event" /><summary type="html">PPT下载：https://www.aliyundrive.com/s/AvVpC1tMbgW 用一节公开课的时间带领大家将考试的常见知识点和考察方式复习一遍，做到听进一道题就能多得一分，快速高效复习。 本次公开课无需报名 需自带纸笔 不预留座位 前排座位先到先得 活动信息： 主讲人： 黄霄 时间： 2021/12/24 18:30 UTC +08:00 活动形式：教四-202 线下授课 同步直播地址：http://live.bilibili.com/14980641</summary></entry><entry><title type="html">SFINAE 的发展史</title><link href="https://info.sast.fun/blog/2021/Phylogeny-of-SFINAE" rel="alternate" type="text/html" title="SFINAE 的发展史" /><published>2021-12-14T00:00:00+00:00</published><updated>2021-12-14T00:00:00+00:00</updated><id>https://info.sast.fun/blog/2021/Phylogeny-of-SFINAE</id><content type="html" xml:base="https://info.sast.fun/blog/2021/Phylogeny-of-SFINAE">&lt;p&gt;说到 C++ 的模板技术，有一个术语不得不提：&lt;strong&gt;SFINAE&lt;/strong&gt; (读作 Sfee-nay，&lt;strong&gt;S&lt;/strong&gt;ubstitution &lt;strong&gt;F&lt;/strong&gt;ailure is &lt;strong&gt;N&lt;/strong&gt;ot &lt;strong&gt;A&lt;/strong&gt;n &lt;strong&gt;E&lt;/strong&gt;rror )。这个技术使得 C++ 这样的静态语言在一定程度上可以实现类似反射的功能 (可以根据类型的特征，表现出不同的行为)。在 C++20 标准概念库发布之后，许多运用到 &lt;strong&gt;SFINAE&lt;/strong&gt; 技术的场景都可以被概念取代，这一古老的技术也许也将退出历史舞台。&lt;/p&gt;

&lt;p&gt;当然，这不是一件值得悲伤的事情，这说明标准委员会在积极地寻求摆脱历史的包袱的途径。&lt;/p&gt;

&lt;p&gt;这篇文章旨在向想要了解 &lt;strong&gt;SFINAE&lt;/strong&gt; 的读者介绍这一技术的发展历史。
&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;什么是-sfinae&quot;&gt;什么是 &lt;strong&gt;SFINAE&lt;/strong&gt;?&lt;/h2&gt;

&lt;p&gt;任何人，看到那样一长串的英文解释，或许都会懵逼。&lt;strong&gt;替换失败不是一个错误？&lt;/strong&gt;什么鬼？&lt;/p&gt;

&lt;p&gt;更加具体地说，这句话的意思是，在&lt;strong&gt;模板实例化的过程中，替换失败不是一个错误。&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;c98-的做法&quot;&gt;C++98 的做法&lt;/h2&gt;

&lt;p&gt;下面我将以判断一个类是否拥有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size_t size()&lt;/code&gt; 方法为例，来深入 &lt;strong&gt;SFINAE&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;我们希望，假如这个类拥有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size&lt;/code&gt; 方法，那么就调用这个方法，否则使用另外一个泛化版本的方法。&lt;/p&gt;

&lt;h3 id=&quot;traits&quot;&gt;traits&lt;/h3&gt;

&lt;p&gt;我们定义一个结构体 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hasSize&lt;/code&gt; 作为类的特征，假如这个类拥有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size&lt;/code&gt; 方法，那么 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hasSize::value&lt;/code&gt; 将会是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true &lt;/code&gt; (或1)，否则为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;hasSize&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Compile-time Boolean&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;no&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Q:&lt;/strong&gt; 你一定注意到了上面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;typedef&lt;/code&gt;，并且对此有些不解。它是做什么的？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; 它是用来做编译期的对错判断的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q:&lt;/strong&gt; 为什么要这么写？直接用函数返回 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; 或 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; 难道不好吗？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; 确实好，但是 C++98 的函数返回值只能在运行时获得。直到 C++11 引入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;constexpr&lt;/code&gt; 之后，这一问题才得到改善。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q:&lt;/strong&gt; 那为什么这么写就能达成我们的目的？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; 我们应该还记得 C 里的一个运算符，它长得有点像函数，但与它有关的求值却全都发生在编译期。那就是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sizeof&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的两个赋值语句，其赋值号右边的值均可以在编译期求得。而看到第二个语句，你一定已经恍然大悟。&lt;/p&gt;

&lt;p&gt;下面就是我们的 &lt;strong&gt;SFINAE&lt;/strong&gt; 登场的时候了。&lt;/p&gt;

&lt;p&gt;我们在结构体内加入另外一个脚手架结构体 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reallyHas&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;reallyHas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们在参数 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;U&lt;/code&gt; 中可以给出函数指针的类型，在参数 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u&lt;/code&gt; 中给出成员函数的具体名字。&lt;/p&gt;

&lt;p&gt;然后给出两个函数 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; 的重载版本：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reallyHas&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yes&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Fallback:&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;no&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(...)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第一个版本返回 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yes&lt;/code&gt;，接受 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;U&lt;/code&gt; 类型的变量为参数，模板参数列表里第一个是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;U&lt;/code&gt;，第二个参数是我们之前的脚手架 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reallyHas&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;第二个版本接受可变长参数。&lt;/p&gt;

&lt;p&gt;匹配 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; 版本的过程中，会发生这样的事：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; 从参数中推导出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;U&lt;/code&gt; 的具体类型，代入模板的第一个参数，然后把所有的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;U&lt;/code&gt; 替换成这个类型。&lt;/li&gt;
  &lt;li&gt;替换 (Substitution) 完毕，接着编译器会去查找实例化后 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; 中和替换后 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;U&lt;/code&gt; 有关的部分（本例中就是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size_t(U::*)() const&lt;/code&gt; 类型的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;U::size&lt;/code&gt;），假如它们不存在，那么这次替换就宣告失败 (Failure)。但替换失败不是一个错误 (Error)，编译器会接着匹配，直到所有候选名单 (&lt;em&gt;candidates&lt;/em&gt;) 的成员都不匹配，才会报错。&lt;/li&gt;
  &lt;li&gt;随着第一个匹配失败，模板去匹配可变长参数版本的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;。这个版本无论如何一定能匹配成功，而它的返回值类型是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;no&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然后我们使用一个枚举常量 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt; 来接受结果：（C++11 之后便被 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;constexpr&lt;/code&gt; 取代）&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这一过程，我们并不需要函数具体的返回值，而只是对返回值的类型作操作。这冥冥之中也印证了一句话：C++的模板是编译期的多态，是类型的多态（或者也可以说，类型和值本身可以等价）。&lt;/p&gt;

&lt;p&gt;当然上面的并不是最终版本，假如我们的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size&lt;/code&gt; 有两种可能的版本：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size_t(U::*)() const&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size_t(U::*)()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;那么我们就无法简单使用上面的做法了。&lt;/p&gt;

&lt;p&gt;下面提供一种更加简洁的做法：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;hasSize&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;no&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;reallyHas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reallyHas&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reallyHas&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;no&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(...)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于 C++ 整形可以隐式转化为指针，我们仍然会先匹配 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yes&lt;/code&gt; 版本的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;enable_if&quot;&gt;enable_if&lt;/h3&gt;

&lt;p&gt;下面我们使用之前的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hasSize&lt;/code&gt; 来帮助我们实现目的。我们来引入另外一个工具人：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enable_if&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;enable_if&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;enable_if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;看起来有点懵？不知道它要干嘛？我们继续实现我们的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getSize&lt;/code&gt; 函数：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enable_if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;getSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;obj has size&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* disable if: */&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enable_if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;getSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;obj has no size&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;两处都得写上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enable_if&lt;/code&gt;，否则会产生二义性。（如果其中一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enable_if&lt;/code&gt; 的参数1为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;，那么返回值是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size_t&lt;/code&gt;，那么另外一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enable_if&lt;/code&gt; 必然没有返回值，所以它会被排除在候选名单之外，假如另外一个函数拥有返回值，那么这个时候编译器将会不清楚应该调用哪个版本的函数，从而产生 error）&lt;/p&gt;

&lt;p&gt;下面来试验一下：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'c'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;输出结果：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;obj has size
4
obj has no size
1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;时间来到-c11&quot;&gt;时间来到 C++11&lt;/h2&gt;

&lt;p&gt;我们在讲述 C++98 的解决方法的时候，已经说过：许多东西到了 C++11 会有更好的解决办法。&lt;/p&gt;

&lt;p&gt;现在我们终于可以介绍 C++11 了。&lt;/p&gt;

&lt;p&gt;其实本来并没有 C++11，它最早的名字叫做 C++0x，因为人们坚信在二十一世纪的前十年 C++11 的标准就能够实现，然而实际上直到2011年，C++11 才正式发布。&lt;/p&gt;

&lt;p&gt;C++11 为模板编程带来了许多的便利。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;首先是编译期表达式类型推导 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decltype&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;接着是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::declval&lt;/code&gt;，这是一个模板函数，它允许我们构造一个类型 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; 的临时量，而无需我们提供参数对其构造。&lt;/li&gt;
  &lt;li&gt;还有我们之前说过的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;constexpr&lt;/code&gt;，也是千呼万唤始出来。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::enable_if&lt;/code&gt;，它进标准了。&lt;/li&gt;
  &lt;li&gt;当然还有新的标准库头文件，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type_traits&lt;/code&gt;，它为我们提供了许许多多方便的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traits&lt;/code&gt;，我们不需要再自己手动实现了。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在 C++11 中，我们将使用另外一个例子——判断一个类是否是可以比较大小的。(这里以小于号为例)&lt;/p&gt;

&lt;p&gt;我们写一个类模板 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isComparable&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;isComparable&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;decltype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(...)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// C++11 initializer list&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; 的参数中用到了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decltype&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::declval&lt;/code&gt;。用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;declval&lt;/code&gt; 来查询是否两个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;U&lt;/code&gt; 类型的变量重载了（或者本身就拥有）&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;operator&amp;lt;&lt;/code&gt;，如果拥有，则匹配成功，否则匹配失败，将会匹配可变长参数版本的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;C++11 版本下我们的许多操作变得更加符合直觉，实现也更加简洁明了。&lt;/p&gt;

&lt;p&gt;试验：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Test1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Test1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Test2&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isComparable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Test1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isComparable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Test2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;除此之外还有另一种方法：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;isComparable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false_type&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 继承而来的 value 成员为 false，下面类似。&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;isComparable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;decltype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true_type&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第一个版本的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isComparable&lt;/code&gt; 默认参数一定要设为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool&lt;/code&gt;，也就是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;operator&amp;lt;&lt;/code&gt; 返回值的类型，原因是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当类模板有默认参数的时候，编译器会更加偏袒那个有默认参数的模板；&lt;/li&gt;
  &lt;li&gt;当带有默认参数的模板与另外一个偏特化模板参数一致的时候，则会优先选择那个偏特化的版本。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;于是当 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[T = Test1]&lt;/code&gt;，偏特化版本模板的第二个参数也是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool&lt;/code&gt;，于是选择了第二个偏特化版本的模板。&lt;/p&gt;

&lt;p&gt;当 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[T = Test2]&lt;/code&gt;，&lt;strong&gt;SFINAE&lt;/strong&gt; 的规则让我们不得不选择第一个版本的模板。&lt;/p&gt;

&lt;h2 id=&quot;c14-泛型-lambda&quot;&gt;C++14 泛型 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lambda&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;C++14 让我们的匿名函数支持 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auto&lt;/code&gt; 类型的参数。它的本质其实就是带有模板括号运算符的仿函数。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// equivalent to:&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Unnamed&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;functor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;因此 &lt;strong&gt;SFINAE&lt;/strong&gt; 的技术也能够适用于它。&lt;/p&gt;

&lt;p&gt;我们可以用泛型 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lambda&lt;/code&gt; 来实现袖珍版的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traits&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;先上效果：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;B&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasLess&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_valid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;decltype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;boolalpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasLess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasLess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasLess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comparableWith&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_valid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;decltype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comparableWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Abc&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comparableWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;72.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comparableWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comparableWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;输出结果：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;true
false
true
false
true
true
false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_valid&lt;/code&gt; 是个啥，好神奇。下面我们就来详细解释一下：&lt;/p&gt;

&lt;p&gt;首先，它是一个&lt;strong&gt;工厂函数&lt;/strong&gt;。产生 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_valid_impl&lt;/code&gt; 类型的对象。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;is_valid_impl&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;private:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;decltype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// fallback&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(...)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;explicit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_valid_impl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Us&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Us&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;us&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;us&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_valid_impl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_valid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_valid_impl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_valid_impl&lt;/code&gt; 的工作原理：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;依靠 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_valid_impl&lt;/code&gt; 构造函数把仿函数对象 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_f&lt;/code&gt; 初始化。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;operator()&lt;/code&gt; 从调用时的实参列表推导出来 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Us...&lt;/code&gt; 将运算委任给 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; 函数。&lt;/li&gt;
  &lt;li&gt;首先匹配第一个版本的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;，这个过程有 &lt;strong&gt;SFINAE&lt;/strong&gt; 的参与：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decltype&lt;/code&gt; 时，将形参代入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_f&lt;/code&gt;，而我们的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_f&lt;/code&gt; 形如：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[] (auto &amp;amp;&amp;amp;x, auto &amp;amp;&amp;amp;y) -&amp;gt; decltype(x &amp;lt; y) { }&lt;/code&gt;，如果参数无法进行某些指定操作，或者参数长度不匹配，那么第一个版本的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; 被 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SFINAE out&lt;/code&gt;。否则匹配成功，返回 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;匹配失败，这个时候就进入第二个版本的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;，其无论如何都会返回 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;由于 C++14 参数推导还不够智能，所以我们这里&lt;strong&gt;不得不使用&lt;/strong&gt;一个工厂函数来帮助我们推导 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F&lt;/code&gt; 的类型，而在后续标准，我们可以不再需要这个工厂函数，而直接使用构造函数了。&lt;/p&gt;

&lt;h2 id=&quot;c17-void_t&quot;&gt;C++17 void_t&lt;/h2&gt;

&lt;p&gt;C++17 引入了一个 类模板&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::void_t&lt;/code&gt;，它可以干啥呢？接受一长串的类型，但自己永远是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void&lt;/code&gt;。它其实就是一个别名模板，长成这样：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;void_t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在可以方便地使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decltype&lt;/code&gt; + 逗号表达式，来完成一长串的判断，而无需判断返回值类型了（有的时候返回值类型是难以判断的，比如返回值类型带有模板参数）。&lt;/p&gt;

&lt;p&gt;下面给出一个终极版 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isComparable&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;isComparable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false_type&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;isComparable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;void_t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;decltype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                                            &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                                            &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                                            &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                                            &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                                            &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;declval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true_type&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;c20-concepts&quot;&gt;C++20 concepts&lt;/h2&gt;

&lt;p&gt;如前言所说，C++20 概念库或给 &lt;strong&gt;SFINAE&lt;/strong&gt; 的时代画上一个句号。那么我们也用概念重写的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isComparable&lt;/code&gt; 为本文画上一个句号。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;concept&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_bool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_convertible_v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;concept&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isComparable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;requires&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;                            
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;concept&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNotComparable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isComparable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们可以这么使用：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 使用不同的概念我们可以提供不同的重载函数版本（即便参数列表相同）&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isComparable&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;is comparable&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isNotComparable&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;is not comparable&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;结果：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;is comparable
is not comparable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然我们可以把 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;concepts&lt;/code&gt; 和上面的那些类模板结合起来，用来做空基类优化，不过那就不是本文要讨论的内容了。&lt;/p&gt;

&lt;h2 id=&quot;后记&quot;&gt;后记&lt;/h2&gt;

&lt;p&gt;尽管我曾在前言说过，我们毋须为 &lt;strong&gt;SFINAE&lt;/strong&gt; 技术的退出而悲伤，但我认为 &lt;strong&gt;SFINAE&lt;/strong&gt; 技术是老一代 C++ 工程师智慧的结晶。二十多年过去，C++ 标准从跛脚逐步开始走向完善，使用 C++ 抽象的方法日趋成熟，我想这其中不无他们的功劳。在模板技术发展的过程中，许多东西都事出偶然，然而如果没有前人的不懈尝试，这些偶然又怎会成为已经发生的必然？&lt;/p&gt;

&lt;p&gt;当然，&lt;strong&gt;SFINAE&lt;/strong&gt; 作为 C++ 本身的一个语言规则，它仍然会在底层发挥作用。不得不直接倚赖底层的东西去解决上层的问题，这是 C++ 过去的缺陷。&lt;/p&gt;

&lt;p&gt;人们始终不停地在探索这个语言的极限，我想这才是 C++ 吸引人的地方。&lt;/p&gt;

&lt;p&gt;如果这篇文章能给读者带来一丝启发，那就再好不过了。&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Jean Guegant: An introduction to C++’s SFINAE concept: compile-time introspection of a class member&lt;/em&gt;&lt;/strong&gt; [https://jguegant.github.io/blogs/tech/sfinae-introduction.html] （我的 &lt;strong&gt;SFINAE&lt;/strong&gt; 启蒙读物）&lt;/p&gt;</content><author><name>Offensive77</name></author><category term="blog" /><category term="SFINAE" /><summary type="html">说到 C++ 的模板技术，有一个术语不得不提：SFINAE (读作 Sfee-nay，Substitution Failure is Not An Error )。这个技术使得 C++ 这样的静态语言在一定程度上可以实现类似反射的功能 (可以根据类型的特征，表现出不同的行为)。在 C++20 标准概念库发布之后，许多运用到 SFINAE 技术的场景都可以被概念取代，这一古老的技术也许也将退出历史舞台。 当然，这不是一件值得悲伤的事情，这说明标准委员会在积极地寻求摆脱历史的包袱的途径。 这篇文章旨在向想要了解 SFINAE 的读者介绍这一技术的发展历史。</summary></entry><entry><title type="html">二十年以来对RSA密码系统攻击综述</title><link href="https://info.sast.fun/blog/2021/Summary-of-RSA" rel="alternate" type="text/html" title="二十年以来对RSA密码系统攻击综述" /><published>2021-12-14T00:00:00+00:00</published><updated>2021-12-14T00:00:00+00:00</updated><id>https://info.sast.fun/blog/2021/Summary-of-RSA</id><content type="html" xml:base="https://info.sast.fun/blog/2021/Summary-of-RSA">&lt;p&gt;这篇文章翻译自Dan Boneh的&lt;strong&gt;&lt;a href=&quot;https://www.ams.org/notices/199902/boneh.pdf&quot;&gt;Twenty Years of Attacks on the RSA Cryptosystem&lt;/a&gt;&lt;/strong&gt;，其中部分译文参考了&lt;a href=&quot;https://paper.seebug.org/727/&quot;&gt;二十年以来对 RSA 密码系统攻击综述 (seebug.org)&lt;/a&gt;&lt;del&gt;（但是这个译文机翻有点重，而且公式也没有了）&lt;/del&gt;。希望通过这篇文章，对目前常见的RSA攻击进行一个总结以及较为深入的了解。 
&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;1分解大整数factoring-large-integers&quot;&gt;1.分解大整数(Factoring Large Integers)&lt;/h2&gt;

&lt;p&gt;我们将模分解称为对RSA的暴力攻击。尽管因数分解算法一直在改进，但在正确使用RSA时，目前的技术水平，还远远没有对RSA的安全性构成威胁。虽然大整数的因式分解是计算数学中最美丽的问题之一，但它不是本文的主题。&lt;/p&gt;

&lt;p&gt;目前最快的分解算法是&lt;strong&gt;General Number Field Sieve(普通数域筛选法)&lt;/strong&gt;，其时间复杂度为&lt;br /&gt;
\(e^{((\sqrt[3]{\frac{64}{9}}+o(1))(\ln{n})^{\frac{1}{3}}(\ln{\ln n})^{\frac{2}{3}}}\)&lt;br /&gt;
学长在服务器上操作下来情况是这样的：16核32线程分解400bits的n，需要4000s；分解505bits的n，需要67小时。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;开放性问题1：给定整数n和e，满足gcd(e, phi(n))=1。定义函数\(f_{e,n}:Z^*_n\to Z^*_n,\ f_{e,n}(x)=x^{\frac{1}{e}}\pmod n\)，是否存在一个多项式时间的算法A，对于给定n，可以计算n的分解同时能访问一些e的oracle \(f_{e,n}(x)\)。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;有学者指出，上述开放性问题，对于一个较小的e来说，答案是否定的。&lt;/p&gt;

&lt;p&gt;下面我们将证明知道公开私钥 d 和分解 N 是等效的。 因此，对于知道 d 的任何一方隐藏 N 的因式分解是没有意义的。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;分解n显然可以很容易计算得到d。&lt;/li&gt;
  &lt;li&gt;若给定d，我们有\(k=e*d-1\)，通过定义我们可以知道，k是\(\phi (n)\)的倍数，我们知道\(\phi(n)\)是偶数，同时有\(k=2^t*r\)，（\(r\)是奇数）。对于\(g\in Z^*_n\)，有\(g^k\equiv1\pmod n\)，并且\(g^{\frac{k}{2}}\)是模n的平方根，由中国剩余定理我们知道，对于n=p*q，这样的数有4个。其中，有两个是正负一，还有两个是\(\pm x\)，其中\(x\equiv1\pmod p,x\equiv-1\pmod q\)。使用后两者，我们就可以通过计算gcd(x-1, n)得到n的分解。有简单的论证表明，如果g是在\(Z^*_n\)中随机取，那么序列\(g^{\frac{k}{2}},g^{\frac{k}{4}},\dots,g^{\frac{k}{2^t}}\pmod n\)的其中一个是平方根且能分解n的概率至少为1/2。序列中的所有元素都可以在\(O(\log_2{n})^3)\)的时间内有效计算。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2两种简单攻击&quot;&gt;2.两种简单攻击&lt;/h2&gt;

&lt;h3 id=&quot;21共模攻击&quot;&gt;2.1共模攻击&lt;/h3&gt;

&lt;p&gt;共模攻击是一种只需要知道多组公钥对，就可以推出明文的攻击，在上一篇中已经介绍，这里就不再赘述。&lt;/p&gt;

&lt;h3 id=&quot;22blinding盲签名攻击&quot;&gt;2.2Blinding——盲签名（攻击）&lt;/h3&gt;

&lt;p&gt;令&amp;lt;n,d&amp;gt;为私钥，&amp;lt;n,e&amp;gt;为公钥。我们想让Bob对m签名，Bob肯定会拒绝。于是我们选择一个随机数\(r\in Z_n^*\)，并且令\(M'=r^em\)，明文改变了，Bob可能愿意在看上去没什么问题的M’上签名。但是此时，我们就可以很简单的得到原本的签名\(S'\equiv(M')^d\pmod n,\ S\equiv S'/r\pmod n\)。&lt;br /&gt;
\(S^e\equiv(S')^e/r^e\equiv(M')^{ed}/r^e\equiv M'/r^e\equiv M\pmod n\)&lt;br /&gt;
不过由于大多数签名方案在签名之前对消息应用”单向散列”算法，这种攻击并不是一个严重的问题。况且，正如标题中攻击在括号里一样，这种盲签名在区块链中应用更广。&lt;/p&gt;

&lt;h2 id=&quot;3低解密指数攻击&quot;&gt;3.低解密指数攻击&lt;/h2&gt;

&lt;p&gt;为了减少解密时间（或签名时间），人们希望使用一个较小的d，而不是随机的d。而这种操作就会导致下面的一些攻击。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;定理(M. Wiener)：令\(n=pq\)，且\(q&amp;lt;p&amp;lt;2q，d&amp;lt;\frac{1}{3}n^{\frac{1}{4}}\)，如果给定公钥，我们可以有效恢复解密指数d。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;证明：我们有等式\(ed-k\phi(n)=1\)，于是&lt;br /&gt;
\(\lvert \frac{e}{\phi(n)}-\frac{k}{d} \rvert=\frac{1}{d\phi(n)}\)&lt;br /&gt;
此时，\(\frac{k}{d}\)是\(\frac{e}{\phi(n)}\)的近似值。尽管我们不知道\(\phi(n)\)，但是我们可以用n近似它。&lt;br /&gt;
\(\phi(n)=n-p-q+1\\\)&lt;br /&gt;
同时，由p、q的不等关系容易得到\(p+q-1&amp;lt;3\sqrt n\)，于是&lt;br /&gt;
\(\lvert n-\phi(n) \rvert&amp;lt;3\sqrt n\)&lt;br /&gt;
我们用上面的不等式代换原有的等式&lt;br /&gt;
\(\lvert \frac{e}{n}-\frac{k}{d} \rvert=\lvert \frac{ed-k\phi(n)-kn+k\phi(n)}{nd} \rvert\\=\lvert \frac{1-k(n-\phi(n))}{nd} \rvert\\\leq\lvert \frac{3k\sqrt{n}}{nd} \rvert\\   
=\frac{3k}{d\sqrt n}\)&lt;/p&gt;

    &lt;p&gt;由于等式\(k\phi(n)=ed-1&amp;lt;ed\)，因为\(e&amp;lt;\phi(n)\)，根据不等式原则可以知道\(k&amp;lt;d&amp;lt;\frac{1}{3}n^{\frac{1}{4}}\)。于是，上面的不等式可以进一步得到&lt;br /&gt;
\(\lvert \frac{e}{n}-\frac{k}{d} \rvert\leq\frac{3k}{d\sqrt n}&amp;lt;\frac{1}{d\sqrt[4]n}&amp;lt;\frac{1}{2d^2}\)&lt;br /&gt;
这是一个经典的逼近关系，分数 \(\frac{k}{d}\) 且 \(k&amp;lt;n\) 在约束\(\log_2n\)内非常逼近 \(\frac{e}{n}\) 。实际上，所有类似这样的分数都是的连分数展开的收敛。因此我们首要做的便是计算的连分数 \(\frac{e}{n}\) 的收敛，其中一个连分数就等于 \(\frac{k}{d}\) 。由于\(ed − kϕ(N)=1\)，所以\(gcd(k,d)=1\)，因此\(\frac{k}{d}\)是一个最简分数。这就是一个可以算出密钥d的线性时间算法。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wiener attack后来被 Boneh and Durfee两人改进，把d的界扩大到了\(d&amp;lt;n^{0.292}\)，这样的结果表明Wiener的界限并不固定。正确的界限可能是\(d&amp;lt;n^{0.5}\)。不过截至到这篇paper发表的时间，这还是一个尚未解决的问题。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;开放性问题2：令\(n=pq，d&amp;lt;n^{0.5}\)，如果给定满足RSA的e、d，是否可以有效的恢复d？&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;4低加密指数&quot;&gt;4.低加密指数&lt;/h2&gt;

&lt;p&gt;为了减少加密或签名验证时间，通常会使用一个小的公钥指数。e的最小可能值为3，但为防止某些攻击，一般推荐使用\(e=2^{16}+1=65537\)。当使用e=65537时，签名验证需要17次乘法运算，而使用随机的e时则需要大约1000次乘法运算。与上一节的攻击不同，当使用一个小e时，应用的攻击不仅仅是得到明文而已。&lt;/p&gt;

&lt;h3 id=&quot;41-coppersmiths-theorem&quot;&gt;4.1 Coppersmith’s Theorem&lt;/h3&gt;

&lt;p&gt;针对RSA低公钥指数最有力的攻击是基于Copper-smith的一个定理，Coppersmith定理有很多应用，这里我们只讨论其中的一些应用，具体如下。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;定理(Coppersmith)：令n为一个整数，\(f\in Z[x]\)是d次的一元多项式，设\(X=N^{\frac{1}{d}-\epsilon}\)其中\(\epsilon\geq0\)，在给定\(&amp;lt;n,f&amp;gt;\)之后Marvin能够有效找到所有满足\(\lvert x_0 \rvert&amp;lt;X\)的整数，运行时间由在维数为\(O(w)\)且\(w=min(1/\epsilon,\log_2N)\)的格上运行的LLL算法所需时间决定。&lt;/p&gt;

    &lt;p&gt;该定理为有效地求模\(N\)的所有小于\(X=N^{1/d}\)的根提供了一种算法，当X越小，算法的运行时间越短。这个定理的强大之处在于它能够找到多项式的小根。当模数为素数时，就目前而言，找不到比使用Coppersmith定理更好的求根算法。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们概述了Coppersmith定理证明背后的主要思想，我们采用由Howgrave-Graham提出的简化方法。给定多项式\(h(x)=\Sigma a_ix^i\in Z[x]\)​，并定义\(\lVert h \rVert^2=\Sigma\lvert a_i \rvert^2\)​。证明需要下面的引理。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;引理：令\(h(x)\in Z[x]\)是d次多项式，同时令X为正整数。假设\(\lVert h(xX) \rVert&amp;lt;N/\sqrt d\)，如果存在\(\lvert x_0 \rvert&amp;lt;X\)满足\(h(x_0)=0\pmod N\)，那么\(h(x_0)=0\)对整数成立。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;证明：从Schwarz不等式观察到：&lt;br /&gt;
\(\lvert h(x_0) \rvert=\lvert \Sigma a_ix_0^i \rvert\\  
=\lvert \Sigma a_iX^i(\frac{x_0}{X})^i \rvert\\  
\leq\Sigma\lvert  a_iX^i(\frac{x_0}{X})^i \rvert\\  
\leq\Sigma\lvert a_iX^i \rvert\\  
\leq\sqrt d\lVert h(xX) \rVert&amp;lt;N\)&lt;/p&gt;

    &lt;p&gt;因为\(h(x_0)=0\pmod N\)，所以我们可以得到结论。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;引理指出，如果\(h\)是一个低范数多项式，则\(h\pmod N\)的所有小根也是\(h\)在整数上的根。引理表明，要找到\(f(x)\pmod N\)的一个小根\(x_0\)，我们需要寻找另一个和\(f\pmod N\)有相同根的低范数多项式\(h\in Z[x]\)，这样就能容易找到在\(h\)整数上的根\(x_0\)。为此，我们可以寻找一个多项式\(g\in Z[x]\)，使得\(h=gf\)具有低范数，即范数小于\(N\)。这相当于寻找具有低范数多项式的整数线性组合\(f,xf,x^2f,\dots,x^rf\)。不过，大多数情况下，并不存在具有足够小的范数的非平凡线性组合。&lt;/p&gt;

&lt;p&gt;Coppersmith找到了解决这个问题的方法：如果\(f(x_0)\equiv 0\pmod N\)成立，那么对于任意\(k\)则有\(f(x_0)^k\equiv 0\pmod {N^k}\)。更一般地，定义以下多项式：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;对于给定m，有&lt;br /&gt;
\(g_{u,v}(x)=N^{m-v}x^uf(x)^v\)&lt;br /&gt;
对任意\(u\geq0,0\leq v\leq m\)，则\(x_0\)是\(g_{u,v}(x)\pmod {N^m}\)的一个根。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;要使用引理4，我们必须找到多项式\(g_{u,v}(x)\)的一个整数线性组合\(h(x)\)，使得\(h(xX)\)的范数小于 \(N^m\)(回想一下\(X\)是满足\(X\leq N^{1/d}\)的\(x_0\)的上界)。由于范数（是\(N\)而不是\(N^m\)）的松弛上界，我们可以证明，对于足够大的\(m\)，总是存在一个线性组合\(h(x)\)满足所要求的界。一旦\(h(x)\)被找到，引理4就说明它有整数根\(x_0\)，于是，\(x_0\)就可以很容易地找到。&lt;/p&gt;

&lt;p&gt;如何有效地找到\(h(x)\)还有待证明，要做到这一点，我们必须先陈述一些关于格的基本事实。 设是\(u_1,\dots,u_w\in Z^w\)线性独立的向量。由\(&amp;lt;u_1,\dots,u_w&amp;gt;\)构成的(满秩)格是\(u_1,\dots,u_w\)的所有整数线性组合的集合。\(L\)的行列式定义为w×w方阵的行列式，其行向量是\(u_1,\dots,u_w\)。&lt;/p&gt;

&lt;p&gt;在我们的例子中，我们把多项式\(g_{u,v}(xX)\)看作向量，并研究了它们所构成的格L。设\(v=0,\dots,m\)，\(u=0,\dots,d-1\)，则格的维数为\(w=d(m+1)\)。例如，当\(f\)是二次一元多项式且\(m=3\)时，得到以下的格：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://116.62.4.97/wp-content/uploads/2021/11/Z2QBR6B1F_0WSOVE75VKS.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;\(*\)元素对应我们忽略的其值的多项式的系数，所有空元素为零。由于矩阵是三角阵，其行列式就是对角线元素乘积。我们需要在这个格中找到短向量。&lt;/p&gt;

&lt;p&gt;Hermite的一个经典结论表明：任意维数为\(w\)的格\(L\)包含一个非零向量\(v\in L\)，它的范数满足\(\lVert v \rVert\leq \gamma_w\det(L)^{1/w}\)，其中\(\gamma_w\)是只依赖于\(w\)的常数。Hermite的界可以用来证明，对于足够大的\(m\)，我们的格包含小于的范数\(N^m\)向量。问题是我们能否有效地在中构造长度小于Hermite界的短向量，而LLL算法恰好是一种有效的算法。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Fact（LLL）：若格\(L\)有一组基\(&amp;lt;u_1,\dots,u_w&amp;gt;\)，通过LLL算法，可以得到一个向量\(v\in L\)满足：&lt;br /&gt;
\(\lVert v \rVert\leq2^{w/4}\det(L)^{1/w}\)&lt;br /&gt;
算法的运行时间是输入长度的四分之一&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;通过LLL算法，我们就可以完成Coppersmith定理的证明。为了保证LLL算法产生的向量满足引理4的界，我们需要满足：&lt;br /&gt;
\(2^{w/4}\det(L)^{1/w}&amp;lt;N^m/\sqrt w\)&lt;br /&gt;
其中\(w=d(m+1)\)是\(L\)的维度。常规计算表明，对于足够大的\(m\)，也能满足约束条件。实际上，当时\(X=N^{\frac{1}{d}-\epsilon}\)，取\(m=O(k/d)\)和\(k=\min(\frac{1}{\epsilon},\log N)\)就足够了。因此，运行时间主要由在维数为\(O(k)\)的格上运行LLL算法所决定。&lt;/p&gt;

&lt;p&gt;一个自然而然的问题，Coppersmith定理能否应用于二元和多元多项式。如果\(f(x,y)\in Z_N[x,y]\)有根\((x_0,y_0)\)且\(\lvert x_0y_0 \rvert\)有适当的界，我们能有效地找到\((x_0,y_0)\)吗？尽管相同的技术似乎仍然适用于某些二元多项式，但它目前还是一个有待证明的开放性问题。随着越来越多的结果依赖于Coppersmith定理的二元扩展，严密的算法将会非常有用。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;开放性问题3：找出可以将 Coppersmith 定理推广到二元多项式的一般条件。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;42-hastad广播攻击&quot;&gt;4.2 Hastad广播攻击&lt;/h3&gt;

&lt;p&gt;其简化版本就是国内常说的低加密指数广播攻击，直接CRT然后开三次方就可以得到明文。一般来说，需要收集k组密文，\(k\geq e\)。&lt;/p&gt;

&lt;p&gt;Hastad提出了一种更强的攻击方法。为了抵御上述的攻击，发送方会做一些简单的防御。Bob在加密之前”填充”消息，而不是直接广播加密后的\(M\)。例如，如果\(M\)是m位长的，Bob可以将\(M_i=i2^m+M\)发送给party \(P_i\)。由于我们获得了不同消息的加密，就无法发起上述攻击。然而，Hastad证明了这种线性填充是不安全的，事实上，他证明了在加密之前对消息应用任何固定多项式都不能阻止攻击。&lt;/p&gt;

&lt;p&gt;假设对于每个参与者\(P_1,\dots,P_k\)，Bob有一个固定的公用多项式\(f_i\in Z_{N_i}[x]\)。为了广播消息\(M\)，Bob将\(f_i(M)\)的加密发送给party \(P_i\)。我们通过窃听知道了\(C_i\equiv f_i(M)^{e_i}\pmod N_i\)，其中\(i=1,\dots,k\)。Hastad证明，如果有足够的参与方，我们就可以从所有的密文中恢复出明文。以下定理是Hastad原始结论的一个更强的版本。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;定理6（Hastad）：令\(N_1,\dots,N_k\)是两两互质的整数，并设\(N_{\min}=\min_i(N_i)\)。设\(g_i\in Z_{N_i}\)是\(k\)个\(d\)次多项式。假设存在唯一的\(M&amp;lt;N_{\min}\)满足&lt;br /&gt;
\(g_i(M)\equiv0\pmod {N_i}\ \ \ i=1,\dots,k\)&lt;br /&gt;
假设\(k&amp;gt;d\)，给定\(&amp;lt;N_i,g_i&amp;gt;_{i=1}^k\)，我们就可以很容易恢复\(M\)。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这个定理表明，当提供了足够多的方程，我们就可以有效地求解以两两互素的整数为模的单变量方程组。令\(g_i\equiv f_i^{e_i}-C_i\pmod {N_i}\)，当有至少\(d\)组数据时，我们就可以恢复\(M\)了。特别的，如果所有的e都一样并且Bob发送线性相关的消息，那么我们只要\(k(k&amp;gt;e)\)组数据，就可以算出明文。&lt;/p&gt;

&lt;p&gt;Hastad的原始定理比上述定理更弱。与\(d\)次多项式不同，Hastad定理需要\(d(d+1)/2\)次多项式。Hastad定理的证明类似于上一节中提到的Coppersmith定理证明。然而，Hastad 格中没有使用\(g\)的幂，因此得到了一个较弱的界 。&lt;/p&gt;

&lt;p&gt;总结这一部分，我们注意到，要正确地防御上述广播攻击，必须使用随机填充方法，而不是使用固定填充方法。&lt;/p&gt;

&lt;h3 id=&quot;43-franklin-reiter相关消息攻击&quot;&gt;4.3 Franklin-Reiter相关消息攻击&lt;/h3&gt;

&lt;p&gt;当Bob用相同的模数发送与Alice相关的加密消息时，Franklin和Reiter发现了一种巧妙的攻击。\(&amp;lt;N,e&amp;gt;\)是Alice的公钥，假设\(M_1,M_2\in Z_N^*\)是两个不同的消息，但存在一个已知的多项式\(f\in Z_N[x]\)，满足\(M_1=f(M_2)\)。为了将\(M_1,M_2\)发送给Alice，Bob可能会简单地对消息进行加密，并发送得到的密文\(C_1,C_2\)。通过证明可以知道，在给定\(C_1,C_2\)的情况下，我们可以很容易地计算出\(M_1,M_2\)。虽然攻击对任意小\(e\)都有效，但为了简化证明，我们给出了\(e=3\)时的引理。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;引理（FR）：令\(e=3\)，\(&amp;lt;N,e&amp;gt;\)是RSA的公钥，且对于\(M_1\ne M_2\in Z_N^*\)，存在一个线性多项式\(f=ax+b\in Z_N[x]\  \ (b\ne0)\)满足\(M_1=f(M_2)\pmod N\)。于是，给定\(&amp;lt;N,e,C_1,C_2,f&amp;gt;\)，我们就可以在\((\log N)^2\)的时间内恢复\(M_1,M_2\)。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;证明：为了保证这部分证明的一般性，我们使用任意的\(e\)来表示它（而不是局限在\(e=3\)）。由于\(C_1\equiv M_1^e\pmod N\)，我们可以知道\(M_2\)是多项式\(g_1(x)=f(x)^e-C_1\in Z_N[x]\)的一个根。同样的，\(M_2\)也是\(g_2(x)=x^e-C_2\in Z_N[x]\)的一个根。这两个多项式都有同一个线性因子\(x-M_2\)，因此我们就可以计算\(\gcd(g_1,g_2)\)，如果结果是线性的，那么我们就找到了\(M_2\)。&lt;/p&gt;

    &lt;p&gt;我们证明了当\(e=3\)时，GCD的结果一定是线性的。多项式因子\(x^3-C_2\)将\(p,q\)都模成一个线性因子和一个不可约二次因子(因为\(\gcd(e,\phi(N))=1\)，所以\(x^3-C_2\)在\(Z_N\)中只有一个根)。因为\(g_2\)不能整除\(g_1\)，所以GCD一定是线性的。对于\(e&amp;gt;3\)的情况，GCD几乎总是线性的。然而，对于一些罕见的\(M_1,M_2\)和\(f\)，有可能得到一个非线性的GCD，在这种情况下攻击会失败。&lt;/p&gt;

    &lt;p&gt;对于\(e&amp;gt;3\)情况，攻击所需时间是\(e\)的平方。因此，只有在使用小的公钥指数\(e\)时才能应用这种攻击。对于大\(e\)，计算GCD的工作令人望而却步。为任意\(e\)设计这样的攻击是一个有趣的问题（尽管可能很困难）。特别是，上面\(g1\)和\(g2\)的GCD能否在\(\log e\)的多项式时间内找到吗？&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;44-coppersmith短填充攻击&quot;&gt;4.4 Coppersmith短填充攻击&lt;/h3&gt;

&lt;p&gt;Franklin-Reiter的攻击可能看起来有点人为。毕竟，为什么Bob要给Alice发送相关消息的加密呢？Coppersmith加强了攻击，并证明了一个关于填充攻击的重要的结论。&lt;/p&gt;

&lt;p&gt;一个简单的随机填充算法可能会通过将几个随机位附加到一个末端来填充一个明文 \(M\)，但是以下攻击指出了这种简单填充的危险。假设Bob向Alice发送了正确填充的加密\(M\)。攻击者Marvin拦截密文并阻止其到达目的地。Bob注意到Alice没有回复他的消息，并决定将重新发送给Alice。他随机填充并传输生成的密文\(M\)。Marvin现在有两个密文，对应于使用两种不同随机填充对同一消息的两次加密。以下定理表明，虽然他不知道使用的填充算法，但Marvin仍能够算出明文。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;定理8：令\(&amp;lt;N,e&amp;gt;\)是RSA的公钥，N长为\(n\)-bits。令\(m=[n/e^2]\)，设\(M\in Z_N^*\)是长最多为\(n-m\)-bits的消息。定义\(M_1=2^mM+r_1,M_2=2^mM+r_2\)，其中\(r_1,r_2\)是不同的整数，且\(0\leq r_1,r_2\leq 2^m\)。如果给定\(&amp;lt;N,e&amp;gt;\)和密文\(C_1,C_2\)，那么就可以有效恢复\(M\)。&lt;/li&gt;
  &lt;li&gt;证明：定义\(g_1(x,y)=x^e-C_1,g_2(x,y)=(x+y)^e-C_2\)。我们知道，当\(y=r_2-r_1\)时，两个多项式有共同的根\(M_1\)。也就是说，\(\Delta=r_2-r_1\)是结果\(h(y)=res_x(g_1,g_2)\in Z_N[y]\)的根。\(h\)的次数最多为\(e^2\)。此外，有\(\lvert \Delta \rvert&amp;lt;2^m&amp;lt;N^{1/e^2}\)，因此\(\Delta\)是\(h\pmod N\)的一个小根，所以我们就可以通过Coppersmith定理计算它。一旦\(\Delta\)求出来，Franklin-Reiter攻击就可以用来计算\(M_2\)，最终算出\(M\)。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当 e = 3 时，只要填充长度小于消息长度的 1/9，就可以发起攻击。 这是一个重要的结论。 但是需要注意，对于 e = 65537 这一推荐值，对于标准的模数大小来说，这种攻击就无效了。&lt;/p&gt;

&lt;h3 id=&quot;45-部分密钥泄露攻击&quot;&gt;4.5 部分密钥泄露攻击&lt;/h3&gt;

&lt;p&gt;设\(&amp;lt;N,d&amp;gt;\)为RSA私钥，假设Marvin通过某种方式知道了\(d\)的一部分，比如说\(d\)的四分之一比特。他能得到\(d\)剩下的部分吗？当相应的公钥指数很小时，答案是肯定的。最近，Boneh，Durfee和Frankel证明了只要\(e&amp;lt;\sqrt N\)，就有可能从它的一小部分位算出的所有部分。这个结论说明了保护整个RSA私钥的重要性。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;定理9(BDF)：令\(&amp;lt;N,d&amp;gt;\)是RSA的私钥，其中\(N\)有n比特。当给定\([n/4]\)个最低有效位，Marvin可以在\(e\log_2e\)的线性时间内恢复所有d。&lt;/li&gt;
  &lt;li&gt;定理10(Coppersmith)：令\(N=pq\)是n比特的RSA模数，给定\(p\)的\(n/4\)个最低有效位或\(p\)的\(n/4\)个最高有效位，我们就可以有效分解\(N\)。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;定理9很容易从定理10推出来，事实上，对于\(e,d\)有：&lt;br /&gt;
\(ed-k(N-p-q+1)=1\)&lt;br /&gt;
由于\(d&amp;lt;\phi(N)\)，可以知道\(0&amp;lt;k\leq e\)，对方程模\(2^{n/4}\)进行约化，令\(q=N/p\)，我们得到&lt;br /&gt;
\((ed)p-kp(N-p+1)+kN\equiv p\pmod{2^{n/4}}\)&lt;br /&gt;
由于我们知道\(d\)的低\(n/4\)的值，于是就有\(ed\pmod{2^{n/4}}\)的值。我们就可以得到一个关于k和p的等式，对于每一个e对应k的可能值，我们求解上面的方程，获得\(p\pmod{2^{n/4}}\)的候选值。对于这些候选值，应用定理10的算法分解N。而所有候选值的总数最多为\(e\log_2e\)，因此在遍历这些值后，N就被成功分解。&lt;/p&gt;

&lt;p&gt;定理9被称为部分密钥泄露攻击，对于更大\(e\)的值，只要\(e&amp;lt;\sqrt N\)，也存在类似的攻击，不过，要实现此种攻击的技术有点复杂。有趣的是，基于离散对数的密码系统，如ELGamal公钥系统，似乎不容易受到部分密钥泄漏攻击的影响。事实上，如果给出\(g^x\pmod p\)和\(x\)位的常数分数，则没有已知的多项式时间算法来计算\(x\)的其余部分。&lt;/p&gt;

&lt;p&gt;为了总结这一节，我们将证明当加密指数\(e\)很小时，RSA系统会泄漏相应私钥\(d\)一半的最高有效位。要了解这一点，再考虑一个方程\(ed-k(N-p-q+1)=1\)，其中\(0&amp;lt;k\leq e\)。给定\(k\)，Marvin可以很容易地计算出：&lt;br /&gt;
\(\hat{d}=[(kN+1)/e]\)&lt;br /&gt;
然后&lt;br /&gt;
\(\lvert \hat{d}-d \rvert\leq k(p+q)/e\leq3k\sqrt N/e&amp;lt;3\sqrt N\)&lt;br /&gt;
因此\(\hat{d}\)是\(d\)的很好的近似值。上面的界表明，对于大多数\(d\)，\(\hat{d}\)的一半高位和d中的相同。由于只有\(e\)个可能的\(k\)，因此Marvin可以构造一个大小为\(e\)的集合，使得该集合中的一个元素等于 d 的最高有效位的一半。\(e=3\)的情况特别有趣，在这种情况下，可以证明总是 k = 2，因此系统完全泄漏了 d 的一半最高有效位。&lt;/p&gt;

&lt;h2 id=&quot;5-执行攻击&quot;&gt;5. 执行攻击&lt;/h2&gt;

&lt;p&gt;我们将注意力转向完全不同的攻击类别。 这些攻击不是攻击 RSA 函数的底层结构，而是侧重于 RSA 的实现。&lt;/p&gt;

&lt;p&gt;待完善&lt;/p&gt;

&lt;p&gt;这些攻击方式在CTF比赛中不容易实现，但是在现实生活中是完全可以存在的，由于本人时间有限，这篇文章也拖了很久，执行攻击这部分就先放一放，以后有时间再补上。&lt;/p&gt;

&lt;h2 id=&quot;6-总结&quot;&gt;6. 总结&lt;/h2&gt;

&lt;p&gt;对RSA系统进行了20年的研究以来，产生了一些有见地的攻击，但还没有发现破坏性的攻击。到目前为止发现的攻击主要说明了在实现RSA时需要避免的陷阱，目前看来，可以信任正确的RSA密码系统实施来提供数字世界中的安全性。&lt;/p&gt;

&lt;p&gt;我们将针对RSA的攻击分为四类：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;(1)利用系统公然误用的基本攻击&lt;/li&gt;
  &lt;li&gt;(2)低私钥指数攻击，此种攻击非常严重，绝不能使用低私钥指数&lt;/li&gt;
  &lt;li&gt;(3)低公钥指数攻击&lt;/li&gt;
  &lt;li&gt;(4)对RSA系统执行时的攻击&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些持续不断的攻击说明，我们对基本数学结构的研究还是不够的。另外，Desmedt和Odlyzko、Joye和Quisquater以及DeJonge和Chaum还提出了一些额外的攻击。在整篇论文中，我们观察到通过在加密或签名之前正确填充消息可以防御许多攻击。&lt;/p&gt;

&lt;p&gt;RSA 的前二十年催生了许多引人入胜的算法。 我希望接下来的二十年同样令人兴奋。&lt;/p&gt;</content><author><name>陈汤林</name></author><category term="blog" /><category term="RSA" /><summary type="html">这篇文章翻译自Dan Boneh的Twenty Years of Attacks on the RSA Cryptosystem，其中部分译文参考了二十年以来对 RSA 密码系统攻击综述 (seebug.org)（但是这个译文机翻有点重，而且公式也没有了）。希望通过这篇文章，对目前常见的RSA攻击进行一个总结以及较为深入的了解。</summary></entry><entry><title type="html">Java 环境安装教程（自动配置环境变量）</title><link href="https://info.sast.fun/blog/2021/java-environment" rel="alternate" type="text/html" title="Java 环境安装教程（自动配置环境变量）" /><published>2021-09-30T00:00:00+00:00</published><updated>2021-09-30T00:00:00+00:00</updated><id>https://info.sast.fun/blog/2021/java-environment-setup</id><content type="html" xml:base="https://info.sast.fun/blog/2021/java-environment">&lt;p&gt;注意，本文包含 Java 开发与运行环境配置，请选择自己需要安装的环境。&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;java-下载安装&quot;&gt;Java 下载安装&lt;/h2&gt;

&lt;p&gt;目前主流的 Java 版本分为 4 种，如下表所示：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;版本&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Oracle Java&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;OpenJDK/OpenJRE&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;区别&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;开发环境&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Oracle JDK&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;OpenJDK&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;带有编译器&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;运行环境&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Oracle JRE&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;OpenJRE&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;无编译器&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;区别&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;闭源，可付费商用&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;依据 GPL 协议开源&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;oracle-jre-8-下载安装&quot;&gt;Oracle JRE 8 下载安装&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;适用平台：Windows、macOS（Intel 芯片版 Mac）&lt;/li&gt;
  &lt;li&gt;演示环境：Windows 10 家庭中文版20H2&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;提示：Oracle JRE 只支持 Java 8 版本。如果程序要求 Java 11 或更高版本，请考虑安装 OpenJRE。&lt;/p&gt;

    &lt;p&gt;如果需要配置 Oracle Java 开发环境（Oracle JDK），请参考：&lt;a href=&quot;https://www.oracle.com/java/technologies/downloads/&quot;&gt;https://www.oracle.com/java/technologies/downloads/&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;配置步骤：
    &lt;ol&gt;
      &lt;li&gt;请务必使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chrome浏览器&lt;/code&gt; 进入 Oracle Java 下载页面 &lt;a href=&quot;https://www.java.com/zh-CN/&quot;&gt;https://www.java.com/zh-CN/&lt;/a&gt; 点击“免费 Java 下载”，再点击跳转后页面的“同意并开始免费下载”按钮，等待安装包下载完成。&lt;/li&gt;
      &lt;li&gt;右键以管理员身份运行安装包，在安装向导界面中点击“安装”并等待程序自动安装。&lt;/li&gt;
      &lt;li&gt;安装完成，如图所示。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/blog/java-environment/java-1.png&quot; alt=&quot;Java 安装完成截图&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;openjdkopenjre-下载安装&quot;&gt;OpenJDK/OpenJRE 下载安装&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;OpenJDK 是 Oracle 根据 GPL 协议开源的 JDK 源码，其中去除了一部分 Oracle 专有代码，因此可能与部分软件存在兼容问题。&lt;/li&gt;
  &lt;li&gt;适用平台：Windows、macOS&lt;/li&gt;
  &lt;li&gt;演示环境：Windows 11&lt;/li&gt;
  &lt;li&gt;提示：Oracle 在网站上提供了 &lt;a href=&quot;https://jdk.java.net/&quot;&gt;OpenJDK 的源码包&lt;/a&gt;，但是不提供安装包，所以需要&lt;del&gt;自己动手编译&lt;/del&gt;下载安装第三方预编译版本。
  Eclipse Adoptium（原 AdoptOpenJDK）是一个比较流行的 OpenJDK 预编译版本，且支持自动配置环境变量。本教程以该版本 OpenJDK 为例。&lt;/li&gt;
  &lt;li&gt;配置步骤：
    &lt;ol&gt;
      &lt;li&gt;使用浏览器进入 Eclipse Adoptium OpenJDK 下载页面（&lt;a href=&quot;https://adoptium.net/archive.html&quot;&gt;https://adoptium.net/archive.html&lt;/a&gt;）选择需要下载的版本（如不确定需要下载哪个版本，推荐下载 OpenJDK 11）。&lt;/li&gt;
      &lt;li&gt;根据自己的系统配置选择对应版本的安装包，若要安装运行环境，请选择 JRE，若要下载开发环境，请选择 JDK。
        &lt;blockquote&gt;
          &lt;p&gt;若官网下载速度慢，可以使用&lt;a href=&quot;https://mirrors.tuna.tsinghua.edu.cn/AdoptOpenJDK/&quot;&gt;清华大学提供的镜像&lt;/a&gt;&lt;/p&gt;
        &lt;/blockquote&gt;
      &lt;/li&gt;
      &lt;li&gt;运行安装包，在安装向导界面中点击“安装”并等待程序自动安装。&lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;使用默认设置完成安装，Windows 系统请确保 Add to PATH 和 Set JAVA_HOME variable 已勾选。&lt;/p&gt;

        &lt;p&gt;&lt;img src=&quot;/assets/img/blog/java-environment/java-installation.png&quot; alt=&quot;安装过程截图&quot; /&gt;&lt;/p&gt;

        &lt;p&gt;&lt;img src=&quot;/assets/img/blog/java-environment/java-installation-completed.png&quot; alt=&quot;安装完毕截图&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;请打开系统终端，执行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java --version&lt;/code&gt; 查看是否安装成功。&lt;/p&gt;

        &lt;p&gt;&lt;img src=&quot;/assets/img/blog/java-environment/java-version.png&quot; alt=&quot;java --version 执行结果&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;OpenJDK 官网：&lt;a href=&quot;https://openjdk.java.net/&quot;&gt;https://openjdk.java.net/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;其他 OpenJDK 预编译包：
    &lt;ul&gt;
      &lt;li&gt;Microsoft 编译版本：&lt;a href=&quot;https://www.microsoft.com/openjdk&quot;&gt;https://www.microsoft.com/openjdk&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;RedHat 编译版本：&lt;a href=&quot;https://developers.redhat.com/products/openjdk/download&quot;&gt;https://developers.redhat.com/products/openjdk/download&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;IBM 编译版本：&lt;a href=&quot;https://developer.ibm.com/languages/java/semeru-runtimes/downloads&quot;&gt;https://developer.ibm.com/languages/java/semeru-runtimes/downloads&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;</content><author><name>linyuqi9, Jisu-Woniu</name></author><category term="blog" /><category term="Java" /><category term="Tutorial" /><summary type="html">注意，本文包含 Java 开发与运行环境配置，请选择自己需要安装的环境。</summary></entry><entry><title type="html">C语言急救车</title><link href="https://info.sast.fun/event/2020/c-final-exam" rel="alternate" type="text/html" title="C语言急救车" /><published>2020-12-26T00:00:00+00:00</published><updated>2020-12-26T00:00:00+00:00</updated><id>https://info.sast.fun/event/2020/C-Final-Exam</id><content type="html" xml:base="https://info.sast.fun/event/2020/c-final-exam">&lt;p&gt;PPT下载地址：https://wwr.lanzoui.com//idSSqjq8huf&lt;/p&gt;

&lt;p&gt;用一节公开课的时间带领大家将考试的常见知识点和考察方式复习一遍，做到听进一道题就能多得一分，快速高效复习。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;本次公开课无需报名 需自带纸笔&lt;/p&gt;

  &lt;p&gt;不预留座位 前排座位先到先得&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;活动信息：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;主讲人： 张帅青&lt;/li&gt;
  &lt;li&gt;时间： 2020/12/26 18:30 UTC +08:00&lt;/li&gt;
  &lt;li&gt;活动形式：线下授课&lt;/li&gt;
&lt;/ul&gt;</content><author><name>xjzsq, matrix72</name></author><category term="event" /><category term="C" /><category term="Final" /><category term="exam" /><summary type="html">PPT下载地址：https://wwr.lanzoui.com//idSSqjq8huf 用一节公开课的时间带领大家将考试的常见知识点和考察方式复习一遍，做到听进一道题就能多得一分，快速高效复习。 本次公开课无需报名 需自带纸笔 不预留座位 前排座位先到先得 活动信息： 主讲人： 张帅青 时间： 2020/12/26 18:30 UTC +08:00 活动形式：线下授课</summary></entry><entry><title type="html">C# 与 .NET 环境配置（2021 年修订）</title><link href="https://info.sast.fun/blog/2020/CS-environment-setup" rel="alternate" type="text/html" title="C# 与 .NET 环境配置（2021 年修订）" /><published>2020-10-25T00:00:00+00:00</published><updated>2020-10-25T00:00:00+00:00</updated><id>https://info.sast.fun/blog/2020/CSharp-environment-setup</id><content type="html" xml:base="https://info.sast.fun/blog/2020/CS-environment-setup">&lt;p&gt;本文将介绍基于 Windows 平台和 .NET 5.0 的 C# 开发环境配置与简易使用方法&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;直接安装-net-cli控制台程序&quot;&gt;直接安装 .NET CLI（控制台程序）&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;访问 .NET 官网&lt;a href=&quot;https://dotnet.microsoft.com/download&quot;&gt;下载页&lt;/a&gt;，选择需要的版本（如 .NET 5.0），选择“Download .NET SDK x64”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;运行安装向导，完成后在终端中输入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet&lt;/code&gt;，将看到以下信息：&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-dotnetcli&quot;&gt; &amp;gt; dotnet
    
 Usage: dotnet [options]
 Usage: dotnet [path-to-application]
    
 Options:
   -h|--help         Display help.
   --info            Display .NET information.
   --list-sdks       Display the installed SDKs.
   --list-runtimes   Display the installed runtimes.
    
 path-to-application:
   The path to an application .dll file to execute.
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在想创建项目的文件夹运行终端，输入&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-dotnetcli&quot;&gt; dotnet new console -o HelloWorldApp
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;等待命令执行完成，这将创建一个 HelloWorldApp 文件夹&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;用代码编辑器（建议使用 &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt;）打开 HelloWorldApp 文件夹，其中的 Program.cs 文件应如下所示：&lt;/p&gt;

    &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

 &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HelloWorldApp&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在 HelloWorldApp 文件夹中，执行&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-dotnetcli&quot;&gt; dotnet run
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;程序将运行并输出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello World!&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;通过-visual-studio-安装以-visual-studio-2019-为例&quot;&gt;通过 Visual Studio 安装（以 Visual Studio 2019 为例）&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;下载 Visual Studio 2019&lt;/p&gt;

    &lt;p&gt;确保电脑符合 Visual Studio 2019 安装&lt;a href=&quot;https://docs.microsoft.com/zh-cn/visualstudio/releases/2019/system-requirements&quot;&gt;系统要求&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;打开 &lt;a href=&quot;https://visualstudio.microsoft.com/&quot;&gt;Visual Studio 官网&lt;/a&gt;选择“下载 Visual Studio Community 2019”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;安装 Visual Studio 2019&lt;/p&gt;

    &lt;p&gt;运行 Visual Studio 2019 安装程序，待其加载数据完成，在“选择工作负荷”界面勾选“.NET 桌面开发”，选择安装位置并点击安装&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;启动 Visual Studio 2019 并登录 Microsoft 账号&lt;/p&gt;

    &lt;p&gt;如果没有账号需在线免费注册。可点击 Visual Studio 官网右上角的头像图标注册。登录过程如遇白屏/加载失败可采用特殊手段联网&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;创建新项目
    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;在启动窗口中选择“创建新项目”，选择“C# 控制台应用程序”，点击“下一步”&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;输入项目名称如“HelloWorldApp”，选择创建位置，点击“下一步”&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;目标框架选择“.NET 5.0”，点击“创建”&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;将自动打开 Program.cs 文件，代码如下：&lt;/p&gt;

    &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
 &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HelloWorldApp&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;按 F5 启动程序，程序将输出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello World!&lt;/code&gt; 与进程退出提示&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;学习资料推荐&quot;&gt;学习资料推荐&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;书籍推荐：&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;《Visual C# 从入门到精通》&lt;/li&gt;
      &lt;li&gt;《C# 入门经典》&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;网站推荐&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;微软官方 C# 文档：&lt;a href=&quot;https://docs.microsoft.com/zh-cn/dotnet/csharp/&quot;&gt;https://docs.microsoft.com/zh-cn/dotnet/csharp/&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;.NET API 浏览器：&lt;a href=&quot;https://docs.microsoft.com/dotnet/api/&quot;&gt;https://docs.microsoft.com/dotnet/api/&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;菜鸟教程：&lt;a href=&quot;https://www.runoob.com/csharp/csharp-tutorial.html&quot;&gt;https://www.runoob.com/csharp/csharp-tutorial.html&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;</content><author><name>EnderHorror, Jisu-Woniu</name></author><category term="blog" /><category term="C#" /><category term=".NET" /><category term="Tutorial" /><summary type="html">本文将介绍基于 Windows 平台和 .NET 5.0 的 C# 开发环境配置与简易使用方法</summary></entry><entry><title type="html">Lisp 入门指北</title><link href="https://info.sast.fun/blog/2020/lisp-introduction" rel="alternate" type="text/html" title="Lisp 入门指北" /><published>2020-10-16T00:00:00+00:00</published><updated>2020-10-16T00:00:00+00:00</updated><id>https://info.sast.fun/blog/2020/lisp-introduction</id><content type="html" xml:base="https://info.sast.fun/blog/2020/lisp-introduction">&lt;h2 id=&quot;原子列表和求值&quot;&gt;原子，列表和求值&lt;/h2&gt;
&lt;p&gt;在 Lisp 中，所有的以空格或是括号分隔的，都是一种叫做&lt;strong&gt;原子&lt;/strong&gt;(atom)的&lt;strong&gt;元素&lt;/strong&gt;(element)，当然，原子也是可被分类的，确切的说，原子可被分为&lt;strong&gt;符号&lt;/strong&gt;和&lt;strong&gt;值&lt;/strong&gt;，这两种东西在 Lisp 的解释器里是不同的。符号是一个词法上的概念，可以类比设想一门自然语言中的名词。而值则是确切存在的自然客属物。一个符号可以指代值，值也就被符号指代，这些我们将会在稍后详细讲解。&lt;/p&gt;

&lt;p&gt;我们现在尝试着将原子进行组合，可以得到什么呢？设想我们已经学到过的组合，比如数组，哈希……同样的的，在 Lisp 中，原子的组合叫做列表，一个列表被一对&lt;strong&gt;闭合&lt;/strong&gt;的大括号所包围。我们来写出一些列表：&lt;/p&gt;

&lt;div class=&quot;language-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Kyoto_Animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;lisp&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;so&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;amazing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fac&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;eql&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fac&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;((())())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如你所见，空也是一个元素，空列表也是列表，列表嵌套列表也是合法的。和我们之前所讲过的基本 λ 计算一样，这给了我们强有力的求值工具。&lt;/p&gt;

&lt;p&gt;那么列表有什么具体的作用呢？简单地说（在这里，我们忽略了少部分的特殊情况），&lt;strong&gt;列表是用于求值(evaluation)的&lt;/strong&gt;。更严格的说，Lisp中的列表是一个 Program。我们来考虑计算语义上的辖域。辖域主要分为三个部分（一个计算系统）：定义式域，命令式域以及表达式域，我们可以说，大部分列表是属于表达式域的，最初的 Lisp 是一个纯表达式域的语言，并且是严格依照代数语义学的理论建构的。当然，现在的 Lisp 经过许多的演化，成为了一个&lt;strong&gt;语族&lt;/strong&gt;。Lisp 拥有众多的方言，Scheme、Racket等等，我们学习的是 Common Lisp，是之后的 Lisp 社区综合了早期的各种 Lisp 方言而尝试制定的一个统一标准。&lt;/p&gt;

&lt;p&gt;还有一个值得注意的问题是，我们发现，对于列表的嵌套深度，Lisp 是没有限度的，这带给了阅读程序的人很大的混淆，比如以下的表达式:&lt;/p&gt;

&lt;div class=&quot;language-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实际上，在一般的Lisp编写时，遵循一种叫做&lt;strong&gt;美观打印&lt;/strong&gt;的原则，即把多个运算对象垂直对齐的写法，因此，符合规范的写法应该是这样的&lt;/p&gt;

&lt;div class=&quot;language-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们继续来讲回求值的概念，现在给出一个列表，我们按照什么样的规则来得出它的值呢，这和我们接下来要介绍的&lt;strong&gt;代换模型&lt;/strong&gt;有关。&lt;/p&gt;

&lt;p&gt;如果按照一般的想法，我们计算 1 + 2 的时候，大脑里思考的是：辨识出这是一个加法运算，然后创造值1，创造值2，运算1与2的加法运算。还有一种思考方式是：创造一个值1，对1应用加法（动词），（宾语）是2，得出结果。&lt;/p&gt;

&lt;p&gt;实际上在一般的语义学分析中采取的都是第一种做法，即关系指代型的语义关系，谓词是第一公民。比如 Bob loves Alice 应写成 Love(Bob,Alice)。&lt;/p&gt;

&lt;p&gt;代换模型采用的是第一种做法，即我们将1和2应用于&lt;strong&gt;加法过程&lt;/strong&gt;。我们定义：一个列表的第一个原子是&lt;strong&gt;运算符&lt;/strong&gt;，其余所有的都是&lt;strong&gt;运算对象&lt;/strong&gt;。代换模型所做的事情是：对运算符求值，得出一个过程，然后分别对运算对象求值，将其应用到运算符过程的形式参数位（回想一下β-规约）。同时，我们也知道， Lisp 出于效率的考虑，采用了急切求值的做法。&lt;/p&gt;

&lt;p&gt;代换模型要求我们不得不采用一种叫做&lt;strong&gt;前缀表示法&lt;/strong&gt;的记法来表示运算，如 (+ 1 2)，这和我们经常使用的&lt;strong&gt;中缀表示法&lt;/strong&gt;是不同的。实际上，我们可以把前缀表示法抽象成一颗&lt;strong&gt;表达式树&lt;/strong&gt;，来看看它在计算机中有什么优势。&lt;/p&gt;

&lt;h2 id=&quot;特殊的列表词法作用域&quot;&gt;特殊的列表，词法作用域&lt;/h2&gt;
&lt;p&gt;但是事情并不总是尽如人意的，我们会发现，拥有一些列表，它们即无法被代换模型合理的解释，也无法被认为是固化的求值顺序，这个时候，我们就会说代换模型&lt;strong&gt;失效&lt;/strong&gt;了。&lt;/p&gt;

&lt;p&gt;我们可以稍稍前进一些，发掘到 Lisp 中少数&lt;strong&gt;命令式&lt;/strong&gt;的风格语句。第一个便是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setq&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt;了。考虑以下的语法规则：&lt;/p&gt;

&lt;div class=&quot;language-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;47&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;num&lt;/span&gt;
                      &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dec&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dec&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上的代码会输出什么值呢？如果我们使用单纯的代换模型来解释以上的代码，我们可以说，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setq&lt;/code&gt;是一个求值过程，他接受了 a 和47，返回了47，接受了 a 和50，返回了50，我们就会有理由的相信，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setq&lt;/code&gt; 是一个接受两个参数并返回第二个参数的过程，但是我们在前两条语句后，如果调用 a 会发现什么呢？&lt;/p&gt;

&lt;p&gt;我们会发现，a 的值被&lt;strong&gt;改变&lt;/strong&gt;了。我们之前说过，函数式编程的核心是使用不变量来计算，一旦引入了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setq&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt;这样特殊的列表，变量也就被引入了Lisp语言之中，这个时候，我们就要用一个更加通用的&lt;strong&gt;环境模型&lt;/strong&gt;来解释列表的求值过程了。&lt;/p&gt;

&lt;p&gt;当然现在也无需立刻介绍这个模型，只需要确知，拥有一个叫做&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setq&lt;/code&gt;的过程，它能够改变变量的值，至于&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt;它仅仅是创建了更深的一层&lt;strong&gt;变量作用域&lt;/strong&gt;而已。&lt;/p&gt;

&lt;p&gt;实际说来，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt;只是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lambda&lt;/code&gt;的一个语法糖而已，至于匿名函数，我们也会在稍后讲到。&lt;/p&gt;

&lt;p&gt;另外两个特殊的列表是条件选择和反引号函数，分别叫做&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cond&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quote&lt;/code&gt;。反引号的作用，实际上就是将&lt;strong&gt;意指&lt;/strong&gt;转为&lt;strong&gt;符号&lt;/strong&gt;。我们本来的 Lisp 语言，是将语法对象确定为一个指代，是有语法学之后的语义学含义的。而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quote&lt;/code&gt;则可以将语法学对象仅仅限制在符号的范畴里，举例说，一旦&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(setq a (quote b))&lt;/code&gt;被求值，那么b的含义被仅仅被限制在了符号层面上，以后我们求值 a，就会仅仅是 b 这个符号。&lt;/p&gt;

&lt;p&gt;至于条件选择的特殊之处，同样也在与它不会将列表里的每一个元素都求值，这样的选择性求值也和原本我们所说的代换模型的&lt;strong&gt;求值规则&lt;/strong&gt;相悖。大家有兴趣的话，可以试着去写一个自己的条件选择函数，来看看能不能运行。&lt;/p&gt;

&lt;p&gt;接下来让我们来讲讲&lt;strong&gt;作用域&lt;/strong&gt;的事情。所谓的作用域，其实就是一个变量约束所能生效的范围。一般而言，&lt;strong&gt;第一个找到的&lt;/strong&gt;定义了变量的过程是作用域的边界，我们来看下面的例子：&lt;/p&gt;

&lt;div class=&quot;language-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这一个过程会输出什么呢？实际上，我们可以说外层的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var&lt;/code&gt;和内层的有着截然不同的词法含义。尽管他们的符号相同，但他们是被互相隔离的。或者说，内层的变量在参与运算的时候&lt;strong&gt;遮蔽&lt;/strong&gt;了外层变量。这便是所谓的&lt;strong&gt;词法作用域&lt;/strong&gt;，设想一个盒子里套另一个盒子的模型，你就很理解为什么这两个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var&lt;/code&gt;会能够共存在一个过程中（实际上并不是一个）。我们作为住在最里面盒子的小人，一旦盒子里有了我们所需要的东西，自然也就不需要打破下一层障壁了。&lt;/p&gt;

&lt;h2 id=&quot;符号取值与匿名函数&quot;&gt;符号，取值与匿名函数&lt;/h2&gt;
&lt;p&gt;Lisp 是一门符号语言，或者说，它是为了解决符号运算而被设计出来的。一个很著名的符号运算例子便是多项式求导，在之后，我们会试着制作出一个完备的多项式求导系统。&lt;/p&gt;

&lt;p&gt;一个符号，是一个语义上的概念。所谓的符号都只是解释器所能够接受的&lt;strong&gt;名称&lt;/strong&gt;。而一个值，则是确实地存储在内存中的地址上的二进制串。我们把一个符号指向一个值的过程，实际上就相当于创建了一个指向内存区域的指针。以后解释器碰到求值这个符号的时候，就会去调取指向的内存。&lt;/p&gt;

&lt;p&gt;为了避免语义的二义性，一个符号往往在一个作用域内只能有一个值，同时，Lisp 还有与众不同的两个特点：其一是并不对符号的大小写加以区分，其二则是一个符号虽然不能同时具有两个值，但确实是能同时拥有一个值和一个过程名称的，这一点将会给我们更深的思考：到底 Lisp 中的过程是以怎样的形式存储和被使用的？&lt;/p&gt;

&lt;p&gt;值得注意的是，Lisp 并不会持久化的存储值的内存，除非是一个&lt;strong&gt;全局&lt;/strong&gt;的赋值，否则 Lisp 会在每次需要的时候重新分配内存来存储变量和值。这一点也往往被人诟病为非常低效，但实际上，一旦我们深入到解释器的细节层面，我们就可以做许多优化来避免运行过慢的局面。&lt;/p&gt;

&lt;p&gt;有了之前的基础后，这边的介绍就简单的多了。事实上我们首先要明确的一点是，所有的过程都是没有名字的，或者说匿名的，我们使用以下的语法规则来创建它们：&lt;/p&gt;

&lt;div class=&quot;language-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;argument_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;function_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一个匿名函数只有在能被求值，或者说，拥有实际参数的时候才合法，所以说，如果我们单单地对一个 lambda 表达式求值是错误的，因为这实际上只是一个指代，而没有被实例化。&lt;/p&gt;

&lt;p&gt;当然，如之前所述，我们也可以给过程指定一个符号的名称，使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defun&lt;/code&gt;就可以完成这样的过程。&lt;/p&gt;

&lt;h2 id=&quot;宏与多值函数&quot;&gt;宏与多值函数&lt;/h2&gt;
&lt;p&gt;什么是宏？这需要我们进一步的去推广列表求值的概念，既然 Lisp 中一切都是符号，那一个列表当然也可以表示为一个符号，一个列表也有它本身的值。所以从这个层面上，我们可以先简单地将宏（Marco）和函数以这样的定义区分开来：&lt;/p&gt;

&lt;p&gt;宏是一种语法结构，它接受参数，返回一个将参数&lt;strong&gt;绑定&lt;/strong&gt;到形参的&lt;strong&gt;表&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;我们可以来参看几个例子&lt;/p&gt;

&lt;div class=&quot;language-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;defmarco&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;reverse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;defmarco&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filter-remainder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;remainder&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;defmarco&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;setq-literal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;symbol&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;symbol&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;',literal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里首先出现了一种新的语法结构，即反引号（backquote）。反引号和引号一样，会抑制之后元素的求值过程，但是与引号不同的是，一旦遇到一个逗号，我们就可以要求解释器去求值逗号之后的一个元素。&lt;/p&gt;

&lt;p&gt;简要说明上面三个例子的意思，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reverse&lt;/code&gt; **宏接受了两个参数，并把它们按照第二个，第一个的方式组合成了序对，而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter-remainder&lt;/code&gt;接受了一个列表和一个除数，用于筛选出列表中能够整除除数的部分。而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setq-literal&lt;/code&gt;则将一个符号的值绑定为另一个符号，我们可以看到由于引号的存在，第二个参数不会被&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setq&lt;/code&gt;求值。&lt;/p&gt;

&lt;p&gt;Lisp 的宏是一个极为强大的语法结构，它给了我们扩展语言表达的能力，或者说，正是有了宏的概念，Lisp 所谓的求值与应用（Evaluation ＆ Application）循环才真正圆满。有了宏，我们可以轻松地用它来制作 Parser，模式匹配和其它语言的解释器，甚至，可以编写 Lisp 自己的解释器（这一点确实难以置信，但我会在之后说明）。&lt;/p&gt;

&lt;p&gt;然后再介绍一个 Lisp 内置的函数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;marcoexpand&lt;/code&gt;，它接受一个宏（需要被引号抑制求值），可以将要用于求值的宏返回的列表打印出来，在你不知道一个宏究竟有什么效果的时候，这个功能非常有用。&lt;/p&gt;

&lt;p&gt;多值是什么？多值即是一个非确定性的计算过程，形式化一点讲，多值就是一个一到多的映射。正常的说，Lisp 中的映射都是一个单射，由于 Curry 化的存在，Lisp 的函数都可以被转化为一个链式的高阶过程链，但是，这里存在着几个特殊的表结构，它们会返回多值。&lt;/p&gt;

&lt;p&gt;其中有一个较为常用的是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;values&lt;/code&gt;过程，它返回所有参数求值后的结果，例如：&lt;/p&gt;

&lt;div class=&quot;language-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;你也可以通过宏来书写自己的多值函数。&lt;/p&gt;</content><author><name>Brethland Yang</name></author><category term="blog" /><category term="Lisp" /><summary type="html">关于 Lisp 和 S-表达式的语法语义介绍</summary></entry><entry><title type="html">招新试题资料</title><link href="https://info.sast.fun/archive/exam-photography-resource" rel="alternate" type="text/html" title="招新试题资料" /><published>2020-10-10T00:00:00+00:00</published><updated>2020-10-10T00:00:00+00:00</updated><id>https://info.sast.fun/archive/exam-photography-resource</id><content type="html" xml:base="https://info.sast.fun/archive/exam-photography-resource">&lt;h1 id=&quot;图1&quot;&gt;图1&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;https://img.matrix72.top/test1.jpg&quot; alt=&quot;图片1&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;图2&quot;&gt;图2&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;https://img.matrix72.top/test2.jpg&quot; alt=&quot;图2&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;图3&quot;&gt;图3&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;https://img.matrix72.top/test3.jpg&quot; alt=&quot;图3&quot; /&gt;&lt;/p&gt;</content><author><name>J.C</name></author><category term="archive" /><summary type="html">图1 图2 图3</summary></entry><entry><title type="html">C/C++环境配置指南</title><link href="https://info.sast.fun/blog/2020/cpp-environment" rel="alternate" type="text/html" title="C/C++环境配置指南" /><published>2020-09-25T00:00:00+00:00</published><updated>2020-09-25T00:00:00+00:00</updated><id>https://info.sast.fun/blog/2020/cpp-environment-setup</id><content type="html" xml:base="https://info.sast.fun/blog/2020/cpp-environment">&lt;p&gt;本文将介绍常见的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C/C++&lt;/code&gt;环境配置方法。&lt;/p&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;devcpp&quot;&gt;Devcpp&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;适用平台：winxp/win7/win10&lt;/li&gt;
  &lt;li&gt;所需文件：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dev C++ 5.11(TDM-GCC 4.9.2)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;配置步骤&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;在安装包上单击右键选择&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;以管理员身份运行&lt;/code&gt;,语言默认选择&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;English&lt;/code&gt;,然后点击&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OK&lt;/code&gt;.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;I Agree&lt;/code&gt; ,之后点击&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next&lt;/code&gt;.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;选择安装路径(建议选择纯英文路径),之后点击&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Install&lt;/code&gt;.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;安装完成后点击&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Finish&lt;/code&gt;并打开&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dev-C++&lt;/code&gt;.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;打开后选择语言为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;简体中文/Chinese&lt;/code&gt;,并点击&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next&lt;/code&gt;.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;选择性配置皮肤等,完成配置.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;选择 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;文件-&amp;gt;新建-&amp;gt;源代码&lt;/code&gt; 或使用快捷键 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+N&lt;/code&gt; 新建一个文件,输入以下C语言代码:&lt;/p&gt;

        &lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello,SAST&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;选择 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;文件-&amp;gt;保存&lt;/code&gt; 或使用快捷键 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+S&lt;/code&gt; 进行保存,保存类型选择 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C source files(*.c)&lt;/code&gt;,按需选择名字与保存位置,点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;保存&lt;/code&gt; 即可.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;保存成功后选择 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;运行-&amp;gt;编译运行&lt;/code&gt; 或使用快捷键 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F11&lt;/code&gt; 将C语言文件编译并运行,输出&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello,SAST&lt;/code&gt;即可.&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;常见问题Q&amp;amp;A&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;为什么我按笔记本键盘上的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F11&lt;/code&gt;没有反应?
部分笔记本需要先按下键盘上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fn&lt;/code&gt; 键,然后再按 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F11&lt;/code&gt; 才有效果;也有部分笔记本需要按住 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fn&lt;/code&gt; 键不放按 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F11&lt;/code&gt; 才有效果.如果均无效,则可能是快捷键冲突,请使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;运行-&amp;gt;编译运行&lt;/code&gt; 的方法编译运行程序.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;vscodevisual-studio-code&quot;&gt;VSCode(Visual Studio Code)&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;写在前面
&lt;strong&gt;Visual Studio Code&lt;/strong&gt;（以下简称&lt;strong&gt;VSCode&lt;/strong&gt;）是一个运行于&lt;strong&gt;Mac OS X&lt;/strong&gt;、&lt;strong&gt;Windows&lt;/strong&gt;和&lt;strong&gt;Linux&lt;/strong&gt;之上的，针对于编写现代&lt;strong&gt;Web&lt;/strong&gt;和云应用的&lt;strong&gt;跨平台源代码编辑器&lt;/strong&gt;。&lt;/p&gt;

    &lt;p&gt;该编辑器集成了所有一款现代编辑器所应该具备的特性，包括&lt;strong&gt;语法高亮（syntax high lighting）&lt;/strong&gt;，&lt;strong&gt;可定制的热键绑定（customizable keyboard bindings）&lt;/strong&gt;，&lt;strong&gt;括号匹配（bracket matching）&lt;/strong&gt;以及&lt;strong&gt;代码片段收集（snippets）&lt;/strong&gt;。同时这款编辑器也拥有对&lt;strong&gt;Git&lt;/strong&gt;的开箱即用的支持。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;适用平台： win7/win10/Mac OS&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;所需文件：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSCode&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;安装步骤 - win10
    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;演示环境：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows 10 专业版 64位（版本：2004）&lt;/code&gt; 官网下载镜像虚拟机&lt;br /&gt;
 同类优秀教程阅读：&lt;a href=&quot;https://0xfaner.top/posts/vscode-config/#more&quot;&gt;Visual Studio Code 配置 C/C++ 环境 | 0xfaner’s Blog&lt;/a&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;下载 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSCode&lt;/code&gt; 安装包  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSCodeSetup-x64-1.49.2.exe&lt;/code&gt; ，可在校科协招新群文件内下载，也可去&lt;a href=&quot;https://code.visualstudio.com/#alt-downloads&quot;&gt;官网下载&lt;/a&gt;，请点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System Installer&lt;/code&gt; 后的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;64 bit&lt;/code&gt; （根据&lt;a href=&quot;[如何查看Windows 系统位数-百度经验 (baidu.com)](https://jingyan.baidu.com/article/27fa73265ed13046f8271f19.html)&quot;&gt;系统位数&lt;/a&gt;选择）下载安装包 。&lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/c-environment/download.png&quot; alt=&quot;附加任务&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;以管理员身份运行安装包，选择安装位置时最好选择不含中文的路径，建议按照图片所示选择附加任务：&lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/c-environment/附加任务.png&quot; alt=&quot;附加任务&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;点击左侧 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Extensions&lt;/code&gt; 按钮或使用快捷键 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+Shift+X&lt;/code&gt; 打开扩展商店。&lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/c-environment/插件.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;搜索 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Chinese&lt;/code&gt; 安装语言包 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Chinese (Simplified) Language Pack for Visual Studio Code&lt;/code&gt; 以汉化 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSCode&lt;/code&gt; ；搜索 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C++&lt;/code&gt; 安装插件 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C/C++&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C/C++ Compile Run&lt;/code&gt; ，以获得 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C/C++&lt;/code&gt; 编辑器环境和按下 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F6&lt;/code&gt; 快速运行体验。（安装完语言包后右下角可能会提示重启，点击重启即可）&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;下载 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TDM-GCC&lt;/code&gt; ，可在校科协招新群群文件内下载，也可去&lt;a href=&quot;https://jmeubank.github.io/tdm-gcc/download/&quot;&gt;官网下载&lt;/a&gt;，点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tdm64-gcc-9.2.0.exe&lt;/code&gt; 下载。&lt;/p&gt;

        &lt;p&gt;&lt;img src=&quot;/assets/img/blog/c-environment/mingw下载.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;以管理员身份运行安装包，去掉 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Check for updated files on the TDM-GCC server&lt;/code&gt; 前面的勾，点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create&lt;/code&gt; 按钮进入安装，选择安装位置时安装路径&lt;strong&gt;不能有英文&lt;/strong&gt;，其他请一路 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next&lt;/code&gt; 即可。&lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/c-environment/TDM-GCC.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;切回 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSCode&lt;/code&gt; 新建一个后缀名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c&lt;/code&gt; 的文件，如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Paste.c&lt;/code&gt; 。（新建文件直接保存，文件类型**选择 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C&lt;/code&gt; **）&lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/c-environment/newfile.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;并输入以下代码：&lt;/p&gt;

    &lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   &lt;span class=&quot;cp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello,SAST!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;然后按下 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F6&lt;/code&gt; 或 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+6&lt;/code&gt; 编译运行，如果能够得到下图结果，则表示环境配置成功！祝贺祝贺！(缩小窗口截图)&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/img/blog/c-environment/hello,sast.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
    &lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;cp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;scanf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%d%d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;然后按下 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F6&lt;/code&gt; 或 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+6&lt;/code&gt; 编译运行，并输入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1 2&lt;/code&gt; ，如果能够得到下图结果，则表示环境配置成功！祝贺祝贺！&lt;br /&gt;
   &lt;img src=&quot;/assets/img/blog/c-environment/a+b.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;进一步配置…
感觉字体太小了?还没法用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+鼠标滚轮&lt;/code&gt; 放大缩小?代码空格太多了不想输入?  进行进一步配置非常重要！点击左下角的设置图标，选择 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;设置&lt;/code&gt; 或使用快捷键 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+,&lt;/code&gt; 打开设置界面。（也可以按&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+Shift+P&lt;/code&gt;，键入&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open Settings (JSON)&lt;/code&gt;，选择 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;首选项：打开设置(json)&lt;/code&gt; ，直接打开 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;settings.json&lt;/code&gt;文件）
&lt;img src=&quot;/assets/img/blog/c-environment/opensetting.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
然后点右上角第一个按钮打开 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;settings.json&lt;/code&gt; 配置文件。&lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/c-environment/setting.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;如果想要使用鼠标滚轮缩放，在大括号内插入以下脚本：&lt;/p&gt;
        &lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;editor.mouseWheelZoom&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
        &lt;p&gt;如果使用代码格式化，请插入以下脚本：&lt;/p&gt;
        &lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;editor.formatOnPaste&quot;: true,
&quot;C_Cpp.clang_format_fallbackStyle&quot;: &quot;{ BasedOnStyle: Microsoft, UseTab: Never, IndentWidth: 4, TabWidth: 4, AllowShortBlocksOnASingleLine: true, AllowShortIfStatementsOnASingleLine: true, AllowShortLoopsOnASingleLine: true, AllowShortCaseLabelsOnASingleLine: true, AllowShortFunctionsOnASingleLine: All, AllowShortLambdasOnASingleLine: All,  }&quot;,
&quot;[cpp]&quot;: {
	&quot;editor.defaultFormatter&quot;: &quot;ms-vscode.cpptools&quot;
},
&quot;editor.formatOnSave&quot;: true,
&quot;C_Cpp.clang_format_style&quot;: &quot;{ BasedOnStyle: Microsoft, UseTab: Never, IndentWidth: 4, TabWidth: 4, AllowShortBlocksOnASingleLine: true, AllowShortIfStatementsOnASingleLine: true, AllowShortLoopsOnASingleLine: true, AllowShortCaseLabelsOnASingleLine: true, AllowShortFunctionsOnASingleLine: All, AllowShortLambdasOnASingleLine: All,  }&quot;,
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
        &lt;p&gt;如果有对 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C++&lt;/code&gt; 版本的要求，请在大括号插入（以 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C++11&lt;/code&gt; 为例）：&lt;/p&gt;

        &lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;&quot;C_Cpp.default.cppStandard&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;c++11&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
        &lt;p&gt;&lt;strong&gt;改变配置后推荐重启VSCode&lt;/strong&gt;，以便设置生效。&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;常见问题Q&amp;amp;A&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;可以配置按 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F5&lt;/code&gt; 调试运行?&lt;br /&gt;
这个可以参考柏老板的配置教程：&lt;a href=&quot;https://blog.0xfaner.site/posts/vscode-cpp-config/&quot;&gt;Visual Studio Code 配置 C/C++ 环境教程 | 0xfaner’s Blog&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;为什么我按下 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F6&lt;/code&gt; 无法成功运行？&lt;br /&gt;
请检查TDM安装位置是否含有中文，中文符号也不行！另外，如果是笔记本的话，看一下是否 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F6&lt;/code&gt; 默认是功能键，尝试使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fn+F6&lt;/code&gt; ( 按住 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fn&lt;/code&gt; 键不放按 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F6&lt;/code&gt; ) 编译运行。另外也可以尝试使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+6&lt;/code&gt; 组合键来编译运行。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ul&gt;</content><author><name>xjzsq</name></author><category term="blog" /><category term="C" /><category term="C++" /><category term="Dev-C++" /><category term="vscode" /><category term="gcc" /><summary type="html">本文将介绍常见的C/C++环境配置方法。</summary></entry><entry><title type="html">Python环境配置指南</title><link href="https://info.sast.fun/blog/2020/python-environment" rel="alternate" type="text/html" title="Python环境配置指南" /><published>2020-09-25T00:00:00+00:00</published><updated>2020-09-25T00:00:00+00:00</updated><id>https://info.sast.fun/blog/2020/python-environment-setup</id><content type="html" xml:base="https://info.sast.fun/blog/2020/python-environment">&lt;p&gt;本文将介绍常见的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Python&lt;/code&gt; 环境配置方法。&lt;/p&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;python3本体安装&quot;&gt;Python3本体安装&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;适用平台：win7/win10/Mac OS X&lt;/li&gt;
  &lt;li&gt;演示环境：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows 10 专业版 64位（版本：2004）&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;配置步骤：
    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;在&lt;a href=&quot;https://www.python.org/downloads/release/python-385/&quot;&gt;Python3.8.5官方下载页&lt;/a&gt;拉到最下方点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows x86-64 executable installer&lt;/code&gt; 或在校科协新生群群文件下载安装包，右击以管理员身份运行此安装包，先在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add Python 3.8 to PATH&lt;/code&gt; 前面的框打勾，然后点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Customize installation&lt;/code&gt; 。
&lt;img src=&quot;/assets/img/blog/python-environment/python-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;之后再点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next&lt;/code&gt; 进入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Advanced Options&lt;/code&gt; 设置页，在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Install for all users&lt;/code&gt; 前打勾，可以选择修改路径名（建议不要修改，如果必须修改请使用英文路径），然后点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Install&lt;/code&gt;&lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/python-environment/python-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;安装完成后，点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Disable path length limit&lt;/code&gt; ，以保证python的配置能够成功写入path，然后关闭即可。
&lt;img src=&quot;/assets/img/blog/python-environment/python-3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;首先打开开始菜单Python文件夹下的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Python 3.8(64-bit)&lt;/code&gt; &lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/python-environment/python-4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

        &lt;p&gt;输入以下python脚本并回车：&lt;/p&gt;

        &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello,SAST!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
        &lt;p&gt;如果能够得到下图结果说明运行正常。&lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/python-environment/python-5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;在命令行写python脚本不便于调试，为了能够保存成.py脚本文件，打开开始菜单Python文件夹下的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDLE&lt;/code&gt; ，点击菜单栏 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;File-&amp;gt;New File&lt;/code&gt; 或使用快捷键 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+N&lt;/code&gt; 新建一个脚本文件，并写入以下脚本：
        &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'SAST'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SAST&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello,SAST!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello,World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
        &lt;p&gt;保存后按 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F5&lt;/code&gt; 或点击菜单栏 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run-&amp;gt;Run Module&lt;/code&gt; 即可运行脚本。&lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/python-environment/python-6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Python的强大得益于第三方库的支持，下面介绍如何安装第三方库：&lt;br /&gt;
按下快捷键 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Win+R&lt;/code&gt; 然后输入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cmd&lt;/code&gt; （或 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;powershell&lt;/code&gt; ）打开命令提示符，然后输入&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install xxx&lt;/code&gt; 即可安装名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xxx&lt;/code&gt; 的第三方库。例如校科协爬虫公开课将会用到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt; 库，则输入：&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;requests
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;

        &lt;p&gt;得到如下结果即为安装成功：&lt;/p&gt;

        &lt;p&gt;&lt;img src=&quot;/assets/img/blog/python-environment/python-7.png&quot; alt=&quot;&quot; /&gt;
（可能有些库的安装需要以管理员身份运行命令行才能成功安装，方法为在左下角开始菜单键上右击选择 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows Powershell（管理员）&lt;/code&gt; ）&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;常见问题Q&amp;amp;A&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;安装过程中提示没有文件夹读写权限?
请以管理员身份运行安装包&lt;/li&gt;
      &lt;li&gt;校科协公开课需要安装哪些第三方库？
需要安装 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;re&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt; 库&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;vscode配置&quot;&gt;VSCode配置&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;适用平台：win7/win10/Mac OS X&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;演示环境：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows 10 家庭版 64位（版本：2004）&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;配置步骤&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;需要前置安装Python3本体&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;按照配置C语言环境VSCode的1~3步安装VSCode&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;在第1步最后打开的插件管理器中：搜索 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Chinese&lt;/code&gt; 安装语言包 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Chinese (Simplified) Language Pack for Visual Studio Code&lt;/code&gt; 以汉化 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSCode&lt;/code&gt; ；搜索 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Python&lt;/code&gt; 安装第一个插件以使VSCode支持python。&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;安装好后打开之前的脚本或新建文件写入以下脚本：&lt;/p&gt;

        &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'SAST'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SAST&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello,SAST!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello,World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;按下 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F5&lt;/code&gt; 选择第一项 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Python File&lt;/code&gt; ，如果结果和下图一致则配置成功。
&lt;img src=&quot;/assets/img/blog/python-environment/python-8.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;常见问题Q&amp;amp;A&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;…&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;pycharm&quot;&gt;PyCharm&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;适用平台：win7/win10/Mac OS X&lt;/li&gt;
  &lt;li&gt;演示环境：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows 10 专业版 64位（版本：2004）&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;配置步骤：
    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;需要前置安装Python3本体&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;在&lt;a href=&quot;https://www.jetbrains.com/pycharm/download/#section=windows&quot;&gt;官方下载页面&lt;/a&gt;点击 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Community&lt;/code&gt; 版本下面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Download&lt;/code&gt; 按钮或在校科协新生群中下载，以管理员身份运行安装包，两次 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next&lt;/code&gt; 后如图打勾，之后继续安装即可。
&lt;img src=&quot;/assets/img/blog/python-environment/pycharm-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;打开软件后按照自身需要选择即可，然后选择 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;New Project&lt;/code&gt; 。
&lt;img src=&quot;/assets/img/blog/python-environment/pycharm-3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;重新输入项目位置和项目名称，重新选择 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Base interpreter&lt;/code&gt; 为上面Python的安装位置。&lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/python-environment/pycharm-4.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;/assets/img/blog/python-environment/pycharm-5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;环境配置需要下载库，可能需要等待一段时间。&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;按 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shift+F10&lt;/code&gt; 或点击菜单 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run-&amp;gt;Run 'main'&lt;/code&gt; 运行，如果运行结果如下图所示，则环境配置成功。&lt;br /&gt;
&lt;img src=&quot;/assets/img/blog/python-environment/pycharm-6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ul&gt;</content><author><name>xjzsq</name></author><category term="blog" /><category term="Python" /><category term="IDLE" /><category term="vscode" /><category term="pycharm" /><summary type="html">本文将介绍常见的Python 环境配置方法。</summary></entry></feed>