Chapter 08

今後の動向

dec9ue
dec9ue
2021.12.18に更新
このチャプターの目次

Modern C++ はここまで変化し続けて来たわけですが、今後どうなるんでしょうか?C++20で採択された仕様を少し見てみようと思います。

モジュール対応

C++のコンパイルはなぜ遅いのか?そりゃヘッダファイルを実装ファイルごとに処理してるからだよ、という問答が容易に成り立つわけです。ヘッダファイルなんて考え方はそろそろ卒業しましょう、モジュールというコンパイル単位で考えていきましょう、ということでC++20からモジュールの機能が導入されました。

.利用側(インポートする側)

import some_mod;

int main() {
    some_lib::SomeObj s;
    some_lib::some_func(s);
}

.ライブラリ側(エクスポートする側)

// 外部へエクスポートするモジュール名
export module some_mod;

// 名前空間とモジュール名は特に関係がない
namespace some_lib
{
    // some_lib::SomeObjがモジュールsome_modとしてエクスポートされる
    export class SomeObj
    {
        int x;
    };
    // some_lib::some_funcがモジュールsome_modとしてエクスポートされる
    export void some_func(SomeObj& s)
    {
       // ...
    }
};

このようなモジュール機構は他のプログラミング言語でも一般的にあるものですが、C++においてはいにしえからの #include ディレクティブとの関係を強く意識した考え方になってはいます。言語機能的にはRust言語でのcrateの概念の影響を強く受けているようにも思われます。

標準ライブラリのモジュール化はC++23以降で検討されるとのことで、2021年時点ではヘッダファイルの #include がなくなることはなさそうです。

コンセプト

イテレータへの対応では関連する begin 関数と end 関数を持っていることなどが条件として求められますが、こういった条件を言語仕様上明示的に取り扱う仕組みがありませんでした。こういった概念を明示的に取り扱うのがコンセプトです。

コンセプトは、テンプレート引数の型が満たすべき制約事項を明示します。

#include <functional>

// 標準ライブラリのiteratorに似た感じのコンセプトを作る
template <class T>
concept Iter = requires (T& x) {
    // これだけのメンバ関数を持っていないといけないという制約
    x.begin();
    x.end();
    x.operator++();
    x.operator*();
};

// TをコンセプトIterにより制約する
template <Iter T,typename ret,typename UnaryOperation>
void iterate(T& x, UnaryOperation f) {
    for(auto i = x.begin(), e = x.end();i!=e;i++)
    {
        f(*i);
    }
}

あっ、しかし、このガイドはテンプレート書く人のためのガイドではないのでした。ただ、これまでテンプレートを書く際にはテンプレートコード内のさまざまな型制約をミスマッチしてはバックトラックするという仕組みで動作しており [1] 、型制約を表現する方法がありませんでした。コンセプトはこの問題を解決し、エラーメッセージを読みやすくする効果が期待されています。

まあ、ざっくりいうとRust言語のTraitみたいな機能ですね。(しかしTraitの方が概念として洗練されていると感じます。。。)

脚注
  1. この仕組みはSFINAE(Substitution failure is not an error)と呼ばれます。 ↩︎