末尾の戻り値の型の構文スタイルを新しいC ++ 11プログラムのデフォルトにする必要がありますか?[閉まっている]


92

C ++ 11は、新しい関数構文をサポートしています。

auto func_name(int x, int y) -> int;

現在、この関数は次のように宣言されます。

int func_name(int x, int y);

新しいスタイルはまだ広く採用されていないようです(gcc stlで言う)

ただし、この新しいスタイルは、新しいC ++ 11プログラムのどこでも優先する必要がありますか、それとも必要な場合にのみ使用しますか?

個人的には、可能な限り古いスタイルを好みますが、スタイルが混在するコードベースはかなり醜いように見えます。


29
それは主decltypeに議論のためにあります。
Cat Plus Plus

CatPlusPlusが言うこと:あなたの例でそれを使用することはあまり意味がありません
stijn 2012年

@Cat Plus Plusこれは、戻り値の型を導出する必要がない限り、C ++ 03のままにすることを意味しますか?
mirk 2012年

1
すべての関数の前に「auto」を指定する必要があるのは醜いです。それはPythonの「def」に対するC ++の際どい答えのようなものですか?
Erik Aronesty

回答:


109

末尾の戻り値の型を使用しなければならない場合があります。特に、ラムダの戻り値の型を指定する場合は、末尾の戻り値の型を介して指定する必要があります。また、戻り値の型decltypeが引数名がスコープ内にあることを必要とするを利用する場合は、末尾の戻り値の型を使用する必要があります(ただし、通常はdeclval<T>この後者の問題を回避するために使用できます)。

末尾の戻り値の型には、他にもいくつかの小さな利点があります。たとえば、従来の関数構文を使用した非インラインメンバー関数定義について考えてみます。

struct my_awesome_type
{
    typedef std::vector<int> integer_sequence;

    integer_sequence get_integers() const;
}; 

my_awesome_type::integer_sequence my_awesome_type::get_integers() const
{
    // ...
}

メンバーtypedefは、クラスの名前が前::get_integersに表示されるまでスコープ内にないため、クラスの修飾を2回繰り返す必要があります。末尾の戻り値の型を使用する場合、型の名前を繰り返す必要はありません。

auto my_awesome_type::get_integers() const -> integer_sequence
{
    // ...
}

この例では、それほど大きな問題ではありませんが、インラインで定義されていない長いクラス名またはクラステンプレートのメンバー関数がある場合、読みやすさに大きな違いが生じる可能性があります。

彼には「新鮮なペイント」:C ++今2012でのセッション、Alisdairメレディスは、あなたが一貫して後続の戻り値の型を使用している場合、あなたのすべての機能の名前がきちんと並んでいることを指摘しました

auto foo() -> int;
auto bar() -> really_long_typedef_name;

私はどこにでも戻り値の型を末尾に使用しましたCxxReflectあなたは、コードの見た目が一貫して使用方法の例を探しているそうだとすれば、あなたは見てそこに(例えば、取ることができますクラス)。type


1
まだコンセンサスが得られていないようですが、新しいスタイルでCxxReflectを見るのは興味深いことです。
mirk 2012年

こんにちは、ジェームズ。この答えは、おそらくC ++ 14標準に照らしてより正確にすることができます。
ドリュードーマン2015年

@DrewDormann何を追加/変更しますか?
underscore_d

アラインメントは実際には大きなプラスです。意味のない「auto」をここで置き換える新しい「func」キーワードがあればいいのにと思いました。
ヨハン・ブール

67

他の人が言ったことに加えて、末尾の戻り値の型thisは、他の方法では許可されていないを使用することもできます

struct A {
  std::vector<int> a;

  // OK, works as expected
  auto begin() const -> decltype(a.begin()) { return a.begin(); }

  // FAIL, does not work: "decltype(a.end())" will be "iterator", but 
  // the return statement returns "const_iterator"
  decltype(a.end()) end() const { return a.end(); }
};

2番目の宣言では、従来のスタイルを使用しました。ただし、thisその位置では許可されていないため、コンパイラーは暗黙的にそれを使用しません。したがって、a.end()は静的に宣言された型を使用して、aどのendオーバーロードがvector<int>呼び出されるかを決定します。これは、最終的に非constバージョンになります。


2
これは(戻り値の型でメンバーを使用する)概念の優れた/きちんとしたデモンストレーションですが、C ++ 14では型の指定が変換なしのインライン定義で完全に冗長であるため、面白いです。これで、完全な戻り値の型の控除を使用できます。:P
underscore_d

27

もう1つの利点は、関数が関数へのポインターを返すときに、末尾の戻り値の型の構文が読みやすくなることです。たとえば、比較します

void (*get_func_on(int i))(int);

auto get_func_on(int i) -> void (*)(int);

ただし、関数ポインタの型エイリアスを導入するだけで、読みやすさが向上すると主張することができます。

using FuncPtr = void (*)(int);
FuncPtr get_func_on(int i);

10

この素晴らしい記事を参照してください:http: //www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.htmlゲームでdecltypeなしでこの構文を使用する場合の非常に良い例:

class Person
{
public:
    enum PersonType { ADULT, CHILD, SENIOR };
    void setPersonType (PersonType person_type);
    PersonType getPersonType ();
private:
    PersonType _person_type;
};

auto Person::getPersonType () -> PersonType
{
    return _person_type;
}

また、Alex Allainの記事「戻り値は関数の前ではなく関数の最後にあるため、クラススコープを追加する必要はありません」という素晴らしい説明も盗まれました。

偶然にクラススコープを忘れて、より大きな災害の場合、別のPersonTypeがグローバルスコープで定義されているこの考えられるケースと比較してください。

typedef float PersonType; // just for even more trouble
/*missing: Person::*/
PersonType Person::getPersonType ()
{
    return _person_type;
}

7
これが「災害」カテゴリに分類されるかどうかはわかりません。タイプが間違っていると、コードはコンパイルされません。ランタイムエラーは悲惨な結果をもたらす可能性があります。コンパイル時のエラーはそれほど多くありません。
James McNellis 2012年

4
@JamesMcNellisは、コンパイラの出力を比較:prog.cpp:13:12: error: prototype for 'PersonType Person::getPersonType()' does not match any in class 'Person'prog.cpp:13:1: error: 'PersonType' does not name a type コンパイラから最初のエラーは、少なくとも私にとっては、理解することは悪いことです。
PiotrNycz

個人的には同意しません。2番目のメッセージは読みづらく、実装を宣言のように見せたいと思います。
jrh
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.