鍍金池/ 教程/ C/ A.4 常量表達式函數(shù)
3.4 本章總結
6.3 基于鎖設計更加復雜的數(shù)據(jù)結構
6.1 為并發(fā)設計的意義何在?
5.2 <code>C++</code>中的原子操作和原子類型
A.7 自動推導變量類型
2.1 線程管理的基礎
8.5 在實踐中設計并發(fā)代碼
2.4 運行時決定線程數(shù)量
2.2 向線程函數(shù)傳遞參數(shù)
第4章 同步并發(fā)操作
2.3 轉移線程所有權
8.3 為多線程性能設計數(shù)據(jù)結構
6.4 本章總結
7.3 對于設計無鎖數(shù)據(jù)結構的指導建議
關于這本書
A.1 右值引用
2.6 本章總結
D.2 &lt;condition_variable&gt;頭文件
A.6 變參模板
6.2 基于鎖的并發(fā)數(shù)據(jù)結構
4.5 本章總結
A.9 本章總結
前言
第10章 多線程程序的測試和調試
5.4 本章總結
第9章 高級線程管理
5.1 內存模型基礎
2.5 識別線程
第1章 你好,C++的并發(fā)世界!
1.2 為什么使用并發(fā)?
A.5 Lambda函數(shù)
第2章 線程管理
4.3 限定等待時間
D.3 &lt;atomic&gt;頭文件
10.2 定位并發(fā)錯誤的技術
附錄B 并發(fā)庫的簡單比較
5.3 同步操作和強制排序
A.8 線程本地變量
第8章 并發(fā)代碼設計
3.3 保護共享數(shù)據(jù)的替代設施
附錄D C++線程庫參考
第7章 無鎖并發(fā)數(shù)據(jù)結構設計
D.7 &lt;thread&gt;頭文件
D.1 &lt;chrono&gt;頭文件
4.1 等待一個事件或其他條件
A.3 默認函數(shù)
附錄A 對<code>C++</code>11語言特性的簡要介紹
第6章 基于鎖的并發(fā)數(shù)據(jù)結構設計
封面圖片介紹
7.2 無鎖數(shù)據(jù)結構的例子
8.6 本章總結
8.1 線程間劃分工作的技術
4.2 使用期望等待一次性事件
8.4 設計并發(fā)代碼的注意事項
D.5 &lt;mutex&gt;頭文件
3.1 共享數(shù)據(jù)帶來的問題
資源
9.3 本章總結
10.3 本章總結
10.1 與并發(fā)相關的錯誤類型
D.4 &lt;future&gt;頭文件
3.2 使用互斥量保護共享數(shù)據(jù)
9.1 線程池
1.1 何謂并發(fā)
9.2 中斷線程
4.4 使用同步操作簡化代碼
A.2 刪除函數(shù)
1.3 C++中的并發(fā)和多線程
1.4 開始入門
第5章 C++內存模型和原子類型操作
消息傳遞框架與完整的ATM示例
8.2 影響并發(fā)代碼性能的因素
7.1 定義和意義
D.6 &lt;ratio&gt;頭文件
A.4 常量表達式函數(shù)
7.4 本章總結
1.5 本章總結
第3章 線程間共享數(shù)據(jù)

A.4 常量表達式函數(shù)

整型字面值,例如42,就是常量表達式。所以,簡單的數(shù)學表達式,例如,23x2-4。可以使用其來初始化const整型變量,然后將const整型變量作為新表達的一部分:

const int i=23;
const int two_i=i*2;
const int four=4;
const int forty_two=two_i-four;

使用常量表達式創(chuàng)建變量也可用在其他常量表達式中,有些事只能用常量表達式去做:

  • 指定數(shù)組長度:
int bounds=99;
int array[bounds];  // 錯誤,bounds不是一個常量表達式
const int bounds2=99;
int array2[bounds2];  // 正確,bounds2是一個常量表達式
  • 指定非類型模板參數(shù)的值:
template<unsigned size>
struct test
{};
test<bounds> ia;  // 錯誤,bounds不是一個常量表達式
test<bounds2> ia2;  // 正確,bounds2是一個常量表達式
  • 對類中static const整型成員變量進行初始化:
class X
{
  static const int the_answer=forty_two;
};
  • 對內置類型進行初始化或可用于靜態(tài)初始化集合:
struct my_aggregate
{
  int a;
  int b;
};
static my_aggregate ma1={forty_two,123};  // 靜態(tài)初始化
int dummy=257;
static my_aggregate ma2={dummy,dummy};  // 動態(tài)初始化
  • 靜態(tài)初始化可以避免初始化順序和條件變量的問題。

這些都不是新添加的——你可以在1998版本的C++標準中找到對應上面實例的條款。不過,新標準中常量表達式進行了擴展,并添加了新的關鍵字——constexpr。

constexpr會對功能進行修改,當參數(shù)和函數(shù)返回類型符合要求,并且實現(xiàn)很簡單,那么這樣的函數(shù)就能夠被聲明為constexpr,這樣函數(shù)可以當做常數(shù)表達式來使用:

constexpr int square(int x)
{
  return x*x;
}
int array[square(5)];

在這個例子中,array有25個元素,因為square函數(shù)的聲明為constexpr。當然,這種方式可以當做常數(shù)表達式來使用,不意味著什么情況下都是能夠自動轉換為常數(shù)表達式:

int dummy=4;
int array[square(dummy)];  // 錯誤,dummy不是常數(shù)表達式

dummy不是常數(shù)表達式,所以square(dummy)也不是——就是一個普通函數(shù)調用——所以其不能用來指定array的長度。

A.4.1 常量表達式和自定義類型

目前為止的例子都是以內置int型展開的。不過,在新C++標準庫中,對于滿足字面類型要求的任何類型,都可以用常量表達式來表示。

要想劃分到字面類型中,需要滿足一下幾點:

  • 一般的拷貝構造函數(shù)。

  • 一般的析構函數(shù)。

  • 所有成員變量都是非靜態(tài)的,且基類需要是一般類型。

  • 必須具有一個一般的默認構造函數(shù),或一個constexpr構造函數(shù)。

后面會了解一下constexpr構造函數(shù)。

現(xiàn)在,先將注意力集中在默認構造函數(shù)上,就像下面清單中的CX類一樣。

清單A.3(一般)默認構造函數(shù)的類

class CX
{
private:
  int a;
  int b;
public:
  CX() = default;  // 1
  CX(int a_, int b_):  // 2
    a(a_),b(b_)
  {}
  int get_a() const
  {
    return a;
  }
  int get_b() const
  {
    return b;
  }
  int foo() const
  {
    return a+b;
  }
};

注意,這里顯式的聲明了默認構造函數(shù)①(見A.3節(jié)),為了保存用戶定義的構造函數(shù)②。因此,這種類型符合字面類型的要求,可以將其用在常量表達式中。

可以提供一個constexpr函數(shù)來創(chuàng)建一個實例,例如:

constexpr CX create_cx()
{
  return CX();
}

也可以創(chuàng)建一個簡單的constexpr函數(shù)來拷貝參數(shù):

constexpr CX clone(CX val)
{
  return val;
}

不過,constexpr函數(shù)只有其他constexpr函數(shù)可以進行調用。CX類中聲明成員函數(shù)和構造函數(shù)為constexpr:

class CX
{
private:
  int a;
  int b;
public:
  CX() = default;
  constexpr CX(int a_, int b_):
    a(a_),b(b_)
  {}
  constexpr int get_a() const  // 1
  {
    return a;
  }
  constexpr int get_b()  // 2
  {
    return b;
  }
  constexpr int foo()
  {
    return a+b;
  }
};

注意,const對于get_a()①來說就是多余的,因為在使用constexpr時就為const了,所以const描述符在這里會被忽略。

這就允許更多復雜的constexpr函數(shù)存在:

constexpr CX make_cx(int a)
{
  return CX(a,1);
}
constexpr CX half_double(CX old)
{
  return CX(old.get_a()/2,old.get_b()*2);
}
constexpr int foo_squared(CX val)
{
  return square(val.foo());
}
int array[foo_squared(half_double(make_cx(10)))];  // 49個元素

函數(shù)都很有趣,如果想要計算數(shù)組的長度或一個整型常量,就需要使用這種方式。最大的好處是常量表達式和constexpr函數(shù)會設計到用戶定義類型的對象,可以使用這些函數(shù)對這些對象進行初始化。因為常量表達式的初始化過程是靜態(tài)初始化,所以就能避免條件競爭和初始化順序的問題:

CX si=half_double(CX(42,19));  // 靜態(tài)初始化

當構造函數(shù)被聲明為constexpr,且構造函數(shù)參數(shù)是常量表達式時,那么初始化過程就是常數(shù)初始化(可能作為靜態(tài)初始化的一部分)。隨著并發(fā)的發(fā)展,C++11標準中有一個重要的改變:允許用戶定義構造函數(shù)進行靜態(tài)初始化,就可以在初始化的時候避免條件競爭,因為靜態(tài)過程能保證初始化過程在代碼運行前進行。

特別是關于std::mutex(見3.2.1節(jié))或std::atomic<>(見5.2.6節(jié)),當想要使用一個全局實例來同步其他變量的訪問時,同步訪問就能避免條件競爭的發(fā)生。構造函數(shù)中,互斥量不可能產(chǎn)生條件競爭,因此對于std::mutex的默認構造函數(shù)應該被聲明為constexpr,為了保證互斥量初始化過程是一個靜態(tài)初始化過程的一部分。

A.4.2 常量表達式對象

目前,已經(jīng)了解了constexpr在函數(shù)上的應用。constexpr也可以用在對象上,主要是用來做判斷的;驗證對象是否是使用常量表達式,constexpr構造函數(shù)或組合常量表達式進行初始化。

且這個對象需要聲明為const:

constexpr int i=45;  // ok
constexpr std::string s(“hello”);  // 錯誤,std::string不是字面類型

int foo();
constexpr int j=foo();  // 錯誤,foo()沒有聲明為constexpr

A.4.3 常量表達式函數(shù)的要求

將一個函數(shù)聲明為constexpr,也是有幾點要求的;當不滿足這些要求,constexpr聲明將會報編譯錯誤。

  • 所有參數(shù)都必須是字面類型。

  • 返回類型必須是字面類型。

  • 函數(shù)體內必須有一個return。

  • return的表達式需要滿足常量表達式的要求。

  • 構造返回值/表達式的任何構造函數(shù)或轉換操作,都需要是constexpr。

看起來很簡單,要在內聯(lián)函數(shù)中使用到常量表達式,返回的還是個常量表達式,還不能對任何東西進行改動。constexpr函數(shù)就是無害的純潔的函數(shù)。

constexpr類成員函數(shù),需要追加幾點要求:

  • constexpr成員函數(shù)不能是虛函數(shù)。

  • 對應類必須有字面類的成員。

constexpr構造函數(shù)的規(guī)則也有些不同:

  • 構造函數(shù)體必須為空。

  • 每一個基類必須可初始化。

  • 每個非靜態(tài)數(shù)據(jù)成員都需要初始化。

  • 初始化列表的任何表達式,必須是常量表達式。

  • 構造函數(shù)可選擇要進行初始化的數(shù)據(jù)成員,并且基類必須有constexpr構造函數(shù)。

  • 任何用于構建數(shù)據(jù)成員的構造函數(shù)和轉換操作,以及和初始化表達式相關的基類必須為constexpr。

這些條件同樣適用于成員函數(shù),除非函數(shù)沒有返回值,也就沒有return語句。

另外,構造函數(shù)對初始化列表中的所有基類和數(shù)據(jù)成員進行初始化。一般的拷貝構造函數(shù)會隱式的聲明為constexpr。

A.4.4 常量表達式和模板

將constexpr應用于函數(shù)模板,或一個類模板的成員函數(shù);根據(jù)參數(shù),如果模板的返回類型不是字面類,編譯器會忽略其常量表達式的聲明。當模板參數(shù)類型合適,且為一般inline函數(shù),就可以將類型寫成constexpr類型的函數(shù)模板。

template<typename T>
constexpr T sum(T a,T b)
{
  return a+b;
}
constexpr int i=sum(3,42);  // ok,sum<int>是constexpr
std::string s=
  sum(std::string("hello"),
      std::string(" world"));  // 也行,不過sum<std::string>就不是constexpr了

函數(shù)需要滿足所有constexpr函數(shù)所需的條件。不能用多個constexpr來聲明一個函數(shù),因為其是一個模板;這樣也會帶來一些編譯錯誤。