C++ の概念で正確に新しいものは何ですか? 私の理解では、それらは を使用することと機能的に同等static_assert
ですが、「良い」方法でコンパイラエラーが読みやすくなります (Bjarne Stroustup が言ったように、10 ページまたはエラーは得られず、1 つだけです)。
基本的に、コンセプトでできることはすべて を使っても実現できるというのは本当static_assert
ですか?
足りないものはありますか?
C++ の概念で正確に新しいものは何ですか? 私の理解では、それらは を使用することと機能的に同等static_assert
ですが、「良い」方法でコンパイラエラーが読みやすくなります (Bjarne Stroustup が言ったように、10 ページまたはエラーは得られず、1 つだけです)。
基本的に、コンセプトでできることはすべて を使っても実現できるというのは本当static_assert
ですか?
足りないものはありますか?
と比較してstatic_assert
、概念は次の理由でより強力です。
static_asserts
std::enable_if
(これは のみでは不可能ですstatic_asserts
)static_asserts
各関数に複数の必要があります)。これにより、次の世界が楽になります。
興味深いパラダイムの構成要素となります。
概念は、特定の要件を満たす型の "クラス" (C++ 用語ではなく "グループ" として) を表現します。Swappable
例として、概念が次のタイプのセットを表現していることがわかります。
std::swap
また、たとえば、std::string
、std::vector
、などはこの要件を満たしているため、次のような関数で互換的に使用できることが簡単にわかります。std::deque
int
template<typename Swappable>
void func(const Swappable& a, const Swappable& b) {
std::swap(a, b);
}
概念は常に C++に存在していました。(おそらく近い将来) に追加される実際の機能では、言語でそれらを表現して強制することができます。
より良い診断に関する限り、当面は委員会を信頼する必要があります. しかし、彼らが「保証する」出力:
error: no matching function for call to 'sort(list<int>&)'
sort(l);
^
note: template constraints not satisfied because
note: `T' is not a/an `Sortable' type [with T = list<int>] since
note: `declval<T>()[n]' is not valid syntax
非常に有望です。
sを使用して同様の出力を実現できることは事実ですが、関数ごとstatic_assert
に異なる s が必要static_assert
になり、非常に速く退屈になる可能性があります。
Container
例として、テンプレート パラメーターを受け取る 2 つの関数の概念によって与えられた要件の量を適用する必要があるとします。両方の関数でそれらを複製する必要があります。
template<typename C>
void func_a(...) {
static_assert(...);
static_assert(...);
// ...
}
template<typename C>
void func_b(...) {
static_assert(...);
static_assert(...);
// ...
}
そうしないと、どの要件が満たされていないかを区別する能力が失われます。
代わりにコンセプトを使用すると、次のように書くだけで、コンセプトを定義して適用できます。
template<Container C>
void func_a(...);
template<Container C>
void func_b(...);
導入されたもう 1 つの優れた機能は、テンプレート制約でテンプレート関数をオーバーロードする機能です。はい、これも で可能ですがstd::enable_if
、それがどれほど醜くなるかは誰もが知っています。
例として、s で動作する関数を作成し、たまたまsContainer
でより適切に動作するバージョンでオーバーロードすることができます。SequenceContainer
template<Container C>
int func(C& c);
template<SequenceContainer C>
int func(C& c);
概念のない代替案は次のようになります。
template<typename T>
std::enable_if<
Container<T>::value,
int
> func(T& c);
template<typename T>
std::enable_if<
SequenceContainer<T>::value,
int
> func(T& c);
間違いなく醜く、おそらくエラーが発生しやすくなります。
上記の例で見たように、構文は間違いなく明確で、概念がより直感的です。これにより、制約を表現するために必要なコードの量を減らし、読みやすさを向上させることができます。
前に見たように、実際には次のようなもので許容レベルに達することができます:
static_assert(Concept<T>::value);
しかし、その時点で、さまざまなstatic_assert
. 概念を使用すると、このトレードオフは必要ありません。
そして最後に、概念は、Haskell の型クラスのような他の機能パラダイムと興味深い類似点を持っています。たとえば、静的インターフェイスを定義するために使用できます。
たとえば、(悪名高い) ゲーム オブジェクト インターフェイスの古典的なアプローチを考えてみましょう。
struct Object {
// …
virtual update() = 0;
virtual draw() = 0;
virtual ~Object();
};
次に、派生オブジェクトのポリモーフィックがあると仮定すると、std::vector
次のことができます。
for (auto& o : objects) {
o.update();
o.draw();
}
すばらしいことですが、複数の継承またはエンティティ コンポーネント ベースのシステムを使用したくない場合は、クラスごとに可能なインターフェイスが 1 つしかないことにほとんど悩まされます。
しかし、実際に静的ポリモーフィズム(それほど動的ではないポリモーフィズム)が必要な場合は、メンバー関数 (および場合によっては他の関数)を必要とするObject
概念を定義できます。update
draw
その時点で、無料の関数を作成できます。
template<Object O>
void process(O& o) {
o.update();
o.draw();
}
その後、他の要件を持つゲーム オブジェクトの別のインターフェイスを定義できます。このアプローチの優れた点は、必要なだけ多くのインターフェイスを開発できることです。
そして、それらはすべてコンパイル時にチェックされ、適用されます。
これは単なるばかげた例 (そして非常に単純化された例) ですが、概念によって、C++ のテンプレートのまったく新しい世界が開かれます。
さらに詳しい情報が必要な場合は、C++ の概念と Haskell 型クラスに関するこの素晴らしい記事を読むことができます。