型推論の文体上の合理的な制限は何ですか?


8

C ++ 0xには、かなり包括的な包括的推論のサポートが追加されています。不必要な繰り返しを避けるために、可能な限りあらゆる場所で使用したいと思っていますが、あちこちで明示的な型情報を削除するのが良い考えかどうか疑問に思っています。このかなり不自然な例を考えてみましょう:

Foo.h

#include <set>

class Foo {
private:

    static std::set<Foo*> instances;

public:

    Foo();
    ~Foo();

    // What does it return? Who cares! Just forward it!
    static decltype(instances.begin()) begin() {
        return instances.begin();
    }

    static decltype(instances.end()) end() {
        return instances.end();
    }

};

Foo.cpp

#include <Foo.h>
#include <Bar.h>

// The type need only be specified in one location!
// But I do have to open the header to find out what it actually is.
decltype(Foo::instances) Foo::instances;

Foo() {

    // What is the type of x?
    auto x = Bar::get_something();

    // What does do_something() return?
    auto y = x.do_something(*this);

    // Well, it's convertible to bool somehow...
    if (!y) throw "a constant, old school";

    instances.insert(this);

}

~Foo() {
    instances.erase(this);
}

これは妥当だと思いますか、それとも完全にばかげていますか?結局のところ、特に動的言語での開発に慣れている場合は、タイプのことをそれほど気にする必要はなく、コンパイラーがタイプシステムの悪質な悪用をキャッチすることを信頼できます。しかし、メソッドシグネチャのエディターサポートに依存している人にとっては、運が悪いので、ライブラリインターフェースでこのスタイルを使用することはおそらく非常に悪い習慣です。

可能なすべての型を暗黙的に記述することにより、コードの追跡が非常に簡単になることがわかります。これにより、C ++の通常の混乱がほぼすべて取り除かれます。もちろんあなたの走行距離は変わるかもしれません、そしてそれは私が聞いてみたいものです。型推論の根本的な使用に対する具体的な長所と短所は何ですか?


実際には、あなたがやるあなたは動的言語で開発している場合は種類を気にする必要性を。何かタイプがおかしい場合は、実行時にコードのそのセクションに到達するまでわかりません。
ラリーコールマン

@ラリー:もちろんです。ただし、使用中のすべてのオブジェクトの正確なタイプと継承チェーンを予測する必要があるという意味ではありません。

回答:


5

私自身は主にPythonプログラマーなので、プログラマーは正確な型を知る必要がないという考え方を共有しています。C ++の場合、特にテンプレート化されたテンプレート化されたテンプレート化されたテンプレートを扱う場合。もちろん、それは静的型付けをやめたからではありません(私はしません-静的な型付けのために、スライスされたパン以来、Haskellを最高のものの1つと考えています)が、正確な型を気にしていません。なぜ、fooやなどの名前を使用する例ではうまく説明されませんget_stuff()。それでは、現実に近いものを選択しましょう。

auto users = get_users();
vector<decltype(users[0])> blocked_users;
/* I'm not a C++ guru, much less C++0x so forgive me if type
   inference and foreach must be combined differently */
for (auto user : users) {
    if (check_name(user.name)) blocked_users.push_back(user)
}

単一の型注釈ではありませんが、それが何をするかは完全に明らかですよね?このコードはのタイプを気にしません。フィードすることができるusersものを含むものを反復するためにいくつかの範囲が必要です。これ以上何もない。他の方法でタイプしなければならないタイプ名の100字を気にする人はいません。namecheck_name

同じことが、意味のある名前を持つほとんどのコードに適用されます。質問の例は明確ではありませんが、コンテキストも識別子も何が起こっているかを示すものではないため、明示的なタイプ名でも明確ではありません。意味のある識別子を使用すると、明示的な型注釈に関係なくコードを理解できます。


3

彼らが推論をもたらした理由は、人々が次のようなコードを書くのを防ぐためでした:

foo().bar().baz().etc();

あなたが書くことができるとき:

Foo f = foo();
Bar b = f.bar();
...

ただし、Fooは長いテンプレートタイプになるため、次のように記述する方が便利です。

auto f = foo();
auto b = f.bar();
...

したがって、原則として、autoを使用すると、最初の例のようなコードの代わりに上記のコードを作成する場合は、必ずそれを使用してください。それ以外の場合は、明示的な定義を追加します。


理にかなっています。auto他の人が明示的であると確信している多くのケースで私は自分自身を使用していますがauto f = new SomethingLong()、式が返すものは明らかであるため、たとえば、本当に何が間違っているのかわかりません。どこに線を引くか迷っています。
Jon Purdy、2011年

これSomethingLongが継承構造の一部でない場合は問題ありませんが、そうでないことが確実でない限り、これはお勧めしません。慎重な側に線を引く方がはるかに簡単です。
dan_waterworth、2011年

2

型推論を常に使用するとは限らないことには十分な理由があります。Haskellには型推論がありますが、通常は関数型を明示的に宣言します。それは部分的に私の開発スタイルの結果です。最初に関数を宣言します。

myFunction :: [Int] -> Int
myFunction xs = undefined
次に、その関数を使用するコードを記述し、型チェックが行われるようにコンパイルします。タイプチェックが完了したら、実装を進めます。

関数型を宣言するもう1つの理由は、宣言が追加のドキュメントとして機能できるためです。このドキュメントがコンパイルごとに検証されるのはおまけです。


2
型優先のアプローチはHaskellでは非常にエレガントですが、C ++に適しているとは思えません。なけれ概念C ++テンプレートの署名は、基本的には何も言う -それはだ、実装タイプと引数を満たさなければならない要件を定義します。テンプレートはコンパイル時にダックタイピングを行うだけです-「動作するかどうか試してみてください」。したがって、動的なダック型言語もそうであるように、型で暗黙的にすることができます。
Dario
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.