13

それが私の質問です。ジェネリック関数またはクラスに渡すことができる型を制限することについてのコンセンサスが何であるかを知りたいだけです。ある時点で読んだことがあると思います。ジェネリックプログラミングをしている場合は、閉じようとするのではなく、開いたままにしておく方が一般的です(ソースを思い出さないでください)。

私はいくつかの内部ジェネリック関数を持つライブラリを書いていますが、ライブラリ内の型のみを使用できるようにする必要があると感じています。それが、それらを使用するための意味だからです。一方で、物事を封じ込める努力がそれだけの価値があるかどうかはわかりません。

誰かがこのトピックに関する統計や権威ある解説の情報源を持っているかもしれませんか?健全な意見にも興味があります。うまくいけば、それはこの質問を完全に無効にするものではありません:\

また、ここで「ベストプラクティス」に相当するタグはありますか?私はそれを具体的には見ませんでしたが、特定のSOトピックのすべてのベストプラクティス情報を提示できると便利なようです...多分そうではなく、単なる考えです。

編集:これまでのところ、私が行っているライブラリの種類は重要であると述べた回答が1つあります。これは、STLコンテナ、可変個引数(タプル)、Boost Fusionなど、その性質のものを処理するデータベースライブラリです。それがどのように関連するかはわかりますが、どちらに進むかを決定するための経験則にも興味があります。

4

5 に答える 5

14

常に可能な限り開いたままにしておきますが、必ず

  • ジェネリックコードで使用する有効なタイプに必要なインターフェイスと動作を文書化します。
  • タイプのインターフェース特性(特性)を使用して、タイプを許可/禁止するかどうかを決定します。タイプ名に基づいて決定しないでください。
  • 誰かが間違ったタイプを使用した場合、合理的な診断を下します。C ++テンプレートは、間違った型でインスタンス化された場合に大量の深くネストされたエラーを発生させるのに最適です。型特性、静的アサーション、および関連する手法を使用すると、より簡潔なエラーメッセージを簡単に生成できます。
于 2011-05-18T16:26:14.587 に答える
3

STL の最も強力なセールス ポイントの 1 つは、STL が非常にオープンであること、そのアルゴリズムがそれ自体が提供するデータ構造だけでなく私のデータ構造でも機能すること、私のアルゴリズムが私のデータ構造だけでなくそのデータ構造でも機能することです。

アルゴリズムをすべての型に対してオープンにしておくことが理にかなっているのか、それとも独自のものに制限することが理にかなっているのかは、作成しているライブラリに大きく依存しますが、それについては何も知りません。

(最初は、広くオープンであることがジェネリック プログラミングのすべてであると答えるつもりでしたが、今では、ジェネリック性には常に限界があり、どこかで線を引く必要があることがわかりました。それが理にかなっていれば。)

于 2011-05-18T16:27:28.467 に答える
3

私のデータベース フレームワークでは、テンプレートを使用せず、単一の基本クラスを使用することにしました。汎用プログラミングとは、任意またはすべてのオブジェクトを使用できることを意味していました。特定の型クラスは、いくつかの一般的な操作よりも重要です。たとえば、文字列と数値が等しいかどうかを比較できます。BLOB (Binary Large OBject) では、別の方法 (別のレコードに保存されている MD5 チェックサムの比較など) を使用する必要がある場合があります。

また、文字列と数値型の間に継承分岐がありました。

継承階層を使用することで、 クラスを使用して任意のフィールドを参照しFieldたり、 などの特殊なクラスを参照したりできますField_Int

于 2011-05-18T17:20:52.213 に答える
2

少なくとも IMO では、正しいことは大まかに概念が試行したことです。指定されたタイプ (または指定されたタイプのセットの 1 つ) を受け取っていることを確認しようとするのではなく、タイプの要件を指定するために最善を尽くします。受け取ったタイプが適切な特性を持ち、テンプレートの要件を満たすことができることを確認します。

概念と同じように、その動機の多くは、要件が満たされていない場合に適切で有用なエラー メッセージを提供することです。最終的に、誰かが要件を満たさない型でテンプレートをインスタンス化しようとすると、コンパイラはエラー メッセージを生成します。問題は、そうではない可能性が高いのですが、エラー メッセージが有効であることを確認するための措置を講じない限り、エラー メッセージがあまり役に立たないことです。

于 2011-05-18T17:18:40.470 に答える
2

問題

クライアントがパブリック ヘッダーで内部関数を確認でき、これらの内部ジェネリック関数の名前が「共通」である場合、クライアントを誤って内部ジェネリック関数を呼び出す危険にさらしている可能性があります。

例えば:

namespace Database
{

// internal API, not documented
template <class DatabaseItem>
void
store(DatabaseItem);
{
    // ...
}

struct SomeDataBaseType {};

}  // Database

namespace ClientCode
{

template <class T, class U>
struct base
{
};

// external API, documented
template <class T, class U>
void
store(base<T, U>)
{
    // ...
}

template <class T, class U>
struct derived
    : public base<T, U>
{
};

}  // ClientCode

int main()
{
    ClientCode::derived<int, Database::SomeDataBaseType> d;
    store(d);  // intended ClientCode::store
}

この例では、 の作成者はmainDatabase::store が存在することさえ知りません。彼は ClientCode::store を呼び出すつもりで、怠惰になり、指定する代わりに ADL に関数を選択させますClientCode::store。結局のところ、彼の への引数storeは と同じ名前空間から来ているstoreので、うまくいくはずです。

うまくいきません。この例では、 を呼び出しますDatabase::store。この呼び出しの内部によってDatabase::storeは、コンパイル時エラーが発生するか、さらに悪いことに実行時エラーが発生する可能性があります。

直し方

関数に一般的な名前を付けるほど、これが発生する可能性が高くなります。内部関数 (ヘッダーに表示する必要がある関数) には、一般的ではない名前を付けてください。または、 のようなサブ名前空間に配置しますdetailsdetails後者の場合、クライアントがADL の目的で関連付けられた名前空間を持っていないことを確認する必要があります。これは通常、クライアントが直接的または間接的に使用するタイプを で作成しないことによって達成されますnamespace details

もっと偏執的になりたい場合は、 でロックダウンを開始してenable_ifください。

内部関数がクライアントにとって有用であると思われる場合、それらはもはや内部関数ではありません。

上記のコード例は大げさではありません。それは私に起こりました。の関数で発生しましたnamespace stdstoreこの例では、過度に一般的です。過度に一般的なコードの典型的な例ですstd::advancestd::distance守るべきものです。そして、それは概念が修正しようとしている問題です。

于 2011-05-18T17:59:40.100 に答える