概念 (C++)

動機

模板類別和函式有必要的對他們使用的型別加上限制。例如STL容器要求它所包含的型別必須是可賦值的。不像動態多型所展現的類別繼承階級，接受Foo&型別的函式可以接受Foo的任何子類別；只要支援所有模板內使用的操作，任何類別都可以被提供作為模版參數。對函式來說，引數的要求是明確的(必需是Foo的子類別)；但是對模版來說，物件必須符合的介面是不明確的。「概念」(concept)提供了一種機制，要求模板參數必須符合特定條件。
引入 concept 的主要目的，是為了改善編譯器發現問題代碼所產生的錯誤訊息。若程式員嘗試在模板中使用不符合其介面需求的型別，編譯器應當回報錯誤。問題在於與模板使用相關的錯誤訊息極難解讀，尤其不利於新手。主要有兩個原因：首先，錯誤訊息往往將模板參數以原名全數列出，造成訊息長度暴增。某些編譯器甚至會對簡單的錯誤產生數千位元組的錯誤訊息。其次，錯誤訊息通常不會立即指出真正發生問題之處。例如，當程式員試圖將不帶有拷貝建構子的型別置入vector中，第一個錯誤幾乎總是指向vector內部使用拷貝建構之處。程式員必須有足夠的經驗和技巧才能夠了解真正的錯誤，是由於使用的型別不滿足vector類的要求(需要拷貝建構子)。
為了解決上述的問題，C++0x 加入了 concept 這種語言特性。Concept 是一種具名的構造，用來描述型別的需求或是條件限制。在 OOP 中，類似的做法是利用基底類別的定義，當作衍生類別的最小需求("is-a"的繼承方式，衍生類別都帶有基底類別的介面)。而 concept 的定義不限於作為模板參數的限制條件，也可以適用於模板定義(如最後的 concept Stack)。
模板使用 concept 的一種方法是以 concept 名稱取代模板型別指示字class或typename。在下面的例子中，若傳入模板函式min的型別不滿足 concept LessThanComparable的要求，編譯時將會產生錯誤，告知使用者具現化(instantiate)模板的型別不符合concept LessThanComparable。
template<LessThanComparable T>
  const T& min(const T &x, const T &y)
  {
    return y < x ? y : x;
  }
相較於上例的簡式用法，更為泛用的concept使用形式如下：
template<typename T> requires LessThanComparable<T>
  const T& min(const T &x, const T &y)
  {
    return y < x ? y : x;
  }
泛用形中，使用關鍵字 requires 作為型別需求表列的開始。需求表列由 concept 所構成，可以利用"非"(!) 與 "且"(&&)的符號，將數個 concept 結合，如同邏輯運算式。若使用者想避免某個特定的 concept 被模板套用，可以用這樣的語法：requires !LessThanComparable<T>。在模板特化或偏特化中，可以指定型別使用特定的模板實作；而否定的 concept 語法，可以顯式地在模板或 concept 中指明被排除的型別條件為何。另外，若需要在需求表列中表達"且"(logical-and)的語意，使用"&&"將多個 concept 連結起來即可。例如若模板中的型別需要設值(assignment)以及拷貝建構(copy-construct)，可以使用requires Assignable<T>&&CopyConstructible<T>。

定義概念

定義 concept 的方式如下：
auto concept LessThanComparable<typename T>{
  bool operator<(T, T);}
此處為 concept LessThanComparable 宣告，說明若型別 T 有一個雙參數的函式：operator <，且函式傳回值為bool，則型別 T 滿足 concept LessThanComparable。函式 operator < 可以是全域或是成員函式。
C++0x 為了避免 concept 的誤用，除非使用者顯式指明，編譯器不會主動認定型別符合 concept (隱式套用 concept)。為了避免繁瑣的指明，此處關鍵字auto 代表只要型別帶有 concept 中指定的操作，它即是符合該 concept 的一個型別。若沒有加上auto，則必須使用concept_map來指明型別符合特定的 concept。
concept 也可以包含多種型別。例如以下的 concept Convertible，表示型別 T 可轉換為 U。
auto concept Convertible<typename T, typename U>{
  operator U(const T&);}
在模板中使用涉及多型別的 concept，必須使用泛用形式：
template<typename U, typename T> requires Convertible<T, U>
  U convert(const T& t)
  {
    return t;
  }
Concept 可以是其他 concept 的構件。在下例中，InputIterator 的第一個參數 Iter 必須符合 concept Regular：
concept InputIterator<typename Iter, typename Value>{
  requires Regular<Iter>;
  Value operator*(const Iter&);
  Iter& operator++(Iter&);
  Iter operator++(Iter&, int);}
另一方面，concept 之間也能帶有衍生關係。如同類的繼承，滿足衍生 concept 的型別也必須滿足基底 concept，語法上也和類繼承相同：
concept ForwardIterator<typename Iter, typename Value> : InputIterator<Iter, Value>{
  // 在此加上 ForwardIterator 的其它要求}
Concept 中也可宣告關聯型別(associated type)，以 typename 宣告。模板使用 concept 時，模板引數必須要提供相關型別的定義。
concept InputIterator<typename Iter>{
  typename value_type;
  typename reference;
  typename pointer;
  typename difference_type;
  requires Regular<Iter>;
  requires Convertible<reference, value_type>;
  reference operator*(const Iter&); // 解參考
  Iter& operator++(Iter&); // 前置遞增
  Iter operator++(Iter&, int); // 後置遞增
  // ...}

對映概念

Concept map 可以將型別"映射"到特定的 concept，告知編譯器使用的型別是"如何"符合 concept。
concept_map InputIterator<char*>{
  typedef char value_type ;
  typedef char& reference ;
  typedef char* pointer ;
  typedef std::ptrdiff_t difference_type ;};
這個 concept_map 定義 char* 符合 concept InputIterator，並且一一聲明所需的關聯型別。
concept_map 可以宣告成模板，下面的例子聲明所有的指針型別都符合 concept InputIterator。
template<typename T> concept_map InputIterator<T*>{
  typedef T value_type ;
  typedef T& reference ;
  typedef T* pointer ;
  typedef std::ptrdiff_t difference_type ;};
concept_map 可以作為一個迷你型別，在其中置入函式的定義與其它用來定義類的相關構件。
concept Stack<typename X>{
  typename value_type;
  void push(X&, const value_type&);
  void pop(X&);
  value_type top(const X&);
  bool empty(const X&);};template<typename T> concept_map Stack<std::vector<T> >{
  typedef T value_type;
  void push(std::vector<T>& v, const T& x) { v.push_back(x); }
  void pop(std::vector<T>& v) { v.pop_back(); }
  T top(const std::vector<T>& v) { return v.back(); }
  bool empty(const std::vector<T>& v) { return v.empty(); }};
在這裡，concept Stack 定義了需要的函式以及關聯型別，而 concept_map 定義如何以 std::vector 實現底層的操作，每個 concept Stack 裡的函式都可以轉接到 std::vector 的函式調用。因此，concept_map能在不改變原型別(類別)的定義下， 完成介面轉換(interface adaptation)。
最後值得一提的是，一些模板的要求可以使用編譯期斷言(static assertion)。它們可以驗證一些模板的要求，不過實際上是針對不同的問題。

公設

C++0x 提供了公設 (axiom) 用來表達概念的語意屬性。舉例來說，我們可以用公設 Associativity 來定義概念 Semigroup:
concept Semigroup< typename Op, typename T> : CopyConstructible<T>{
  T operator()(Op, T, T);

  axiom Associativity(Op op, T x, T y, T z)
  {
    op(x, op(y, z)) == op(op(x, y), z);
  }}
編譯器可以利用公設所表達的語意做些原本不被允許的優化，因為這些優化可能會在程式可見的行為上有副作用 (其除了少數的例外，其中之一是回返值優化 (RVO))。在上述的例子中，編譯器可能會重新安排 operator() 呼叫的次序。前提是 Op 和 T 與概念 Semigroup 有對映關係。
公設也能在軟體驗證，軟體測試以及其它程式分析和轉換上有所幫助。