typedefを使用する場合


14

C ++でtypedefを使用すべきかどうか、またいつ使用すべきかについて少し混乱しています。読みやすさと明快さのバランスをとる行為だと思います。

typedefを使用しないコードサンプルを次に示します。

int sum(std::vector<int>::const_iterator first, 
        std::vector<int>::const_iterator last)
{
    static std::map<std::tuple<std::vector<int>::const_iterator,
                               std::vector<int>::const_iterator>,
                    int> lookup_table;

    std::map<std::tuple<std::vector<int>::const_iterator,
                        std::vector<int>::const_iterator>, int>::iterator lookup_it =
        lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

かなりいIMO。そこで、関数内にtypedefをいくつか追加して、見栄えを良くします。

int sum(std::vector<int>::const_iterator first, 
        std::vector<int>::const_iterator last)
{
    typedef std::tuple<std::vector<int>::const_iterator,
                       std::vector<int>::const_iterator> Lookup_key;
    typedef std::map<Lookup_key, int> Lookup_table;

    static Lookup_table lookup_table;

    Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

コードはまだ少し不器用ですが、ほとんどの悪夢のような素材を取り除きます。しかし、まだintベクトルイテレータがあり、このバリアントはそれらを取り除きます:

typedef std::vector<int>::const_iterator Input_iterator;

int sum(Input_iterator first, Input_iterator last)
{
    typedef std::tuple<Input_iterator, Input_iterator> Lookup_key;
    typedef std::map<Lookup_key, int> Lookup_table;

    static Lookup_table lookup_table;

    Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

これはきれいに見えますが、それでも読み取り可能ですか?

いつtypedefを使用すべきですか?悪夢のようなタイプがあるとすぐに?複数回発生するとすぐに?どこに置いたらいいですか?関数のシグネチャで使用するか、実装に保持する必要がありますか?


1
複製ではありませんが、私の質問Programmers.stackexchange.com/questions/130679/…に
。– c0da

typedef Input_iterator std::vector<int>::const_iterator;は後方です
ヨハンソン

1
読みやすさと明確さの違いはありますか?
ニール

#define十分ではないとき。
トーマスエディング

回答:


6

最後の例は非常に読みやすいですが、typedefの定義場所によって異なります。ローカルスコープのtypedef(2番目の例のように)は、ほとんど常にIMVHOの勝ちです。

3番目の例は今でも最も気に入っていますが、名前付けについて考え、コンテナの意図を示すイテレータの名前を付けたい場合があります。

別のオプションは、関数からテンプレートを作成し、異なるコンテナでも機能するようにすることです。の線に沿って

template <typename Input_iterator> ... sum(Input_iterator first, Input_iterator last) 

これもまたSTLの精神に基づいています。


2

typedef関数に相当する変数宣言と考えてください:そこにあるので...

  • ...同じタイプを再利用するときに、最初の2つの例で同じように繰り返す必要はありません。
  • ...そのタイプの厄介な詳細を隠すことができるため、常に表示されているわけではありません。
  • ...型への変更が、使用されるすべての場所に反映されることを確認します。

個人的に、std::vector<int>::const_iterator繰り返しタイプのような長いタイプ名を読まなければならない場合、私はoverみます。

3番目の例は不必要に繰り返されることはなく、読みやすくなっています。


1

typedef宣言は、カプセル化と本質的に同じ目的を果たします。そのため、クラスと同じ命名規則に従って、ほとんど常にヘッダーファイルに最適です。

  • が必要な場合typedef、特に引数で使用されている例のように、呼び出し側もそうなる可能性があります。
  • 何らかの理由でタイプを変更する必要がある場合(独自のクラスに置き換えるなど)、1か所で変更するだけで済みます。
  • これにより、複雑な型を繰り返し記述するためにミスを起こしにくくなります。
  • 不要な実装の詳細を隠します。

余談ですが、メモ化コードは、次のようにさらに抽象化すると、ずっときれいになります。

if (lookup_table.exists(first, last))
    return lookup_table.get(first, last);

あなたの提案はよりきれいに見えるかもしれませんが、検索を2回行うことで時間を無駄にします。
デレクレッドベター

うん、それは意図的なトレードオフでした。ただし、特にスレッドの安全性を心配していない場合は、ほぼ同じクリーンな単一のルックアップでそれを行う方法があります。
カールビーレフェルト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.