web-dev-qa-db-ja.com

「const T&」よりも「T const&」を好む人がいるのはなぜですか?

ですから、const T&およびT const&は同一で、どちらもconst Tへの参照を意味します。どちらの場合も、参照も定数です(ポインターとは異なり、参照は再割り当てできません)。私のやや限られた経験では、ほとんどのC++プログラマーがconst T&、しかし私はT const&。私が使う const T&単に私がその方法でそれを学んだからであり、それゆえT const&は、私には少しおかしく見えます。使用するバリアントを使用する理由は何ですか?コーディング標準が他のバリアントの使用を義務付けている組織で働いている人はいますか?

編集する
回答に基づいて、2つの中から1つを選択する理由の1つは、コンパイラのように読む(右から左)か、英語のように読む(左から右)かです。コンパイラのようにそれを読み取る場合、「T const&」は「&(参照)const(定数へ)T(タイプT)」として読み取られます。左から右へ英語のように読むと、「const T&」は「参照形式のT型の定数オブジェクト」として読み取られます。私はそれを英語の散文のように読むことを好みますが、コンパイラーのように解釈することには確かに意味があります。

誰も組織やコーディング標準の質問に答えたことはありませんが、一貫性を保つために努力しているかもしれませんが、ほとんどの組織はどちらか一方を他方に義務付けていないのではないかと私は強く思います。

57

一部の人々は単に宣言を右から左に読むことを好むと思います。 constは左側のトークンに適用されます。ただし、そこに何もない場合、右側のトークンに適用されます。したがって、const T&には "except"節が含まれ、おそらくより複雑に考えることができます(実際にはどちらも理解しやすいはずです)。

比較:

const T* p;  (pointer to T that is const)
T const* p;  (pointer to const T) //<- arguable more natural to read
T* const p;  (const pointer to T)
43
UncleBens

これにより、const/volatile修飾子が複数ある場合に違いが生じます。次に、それを型の左側に置くことはまだ有効ですが、宣言全体の一貫性が損なわれます。例えば:

T const * const *p;

は、pがconst Tへのconstポインターへのポインターであり、右から左へ一貫して読み取ることを意味します。

const T * const *p;

は同じことを意味しますが、一貫性は失われ、左端のconst/volatileはT *ではなくTにのみバインドされることを覚えておく必要があります。

26
Tomek

このディスカッションが面白いと思うなら、おそらくDan Saksによる this の記事が面白いでしょう。それは直接あなたの質問に対処しませんが、彼が好む理由を説明します

VP const foo[];

const VP foo[];

与えられたからです

typedef void *VP;

上記の2番目の例は、

const void *foo[];  // Wrong, actually: void *const foo[];

しかし、最初の例は誤解しにくいです。

7
Larry Engholm

私の推論は次のとおりです:

"const T&"と書けば、舌がうまくロールオフするように見えますが、そうすると、あいまいな "定数T参照"になります。私はこれがコードの理解しやすさに問題を引き起こすことを何度も経験しました。これにより、経験の浅い人でも、何かが何を意味するのか、またはより複雑な型を宣言する方法を誤って解釈することができました。

今のところどのような例も考えられませんが、「T const&」の代わりに「const T&」を使用する習慣が問題の原因である型宣言とconstnessに関する質問に何度も答えました。私もそのように書いていました。プロジェクトのコード標準の指導と作成を担当するシニア開発者になったとき、全員に「T const&」を使用するように強制すると、エントリレベルの開発者にとってはるかに簡単になります。私はかなり些細な新人の間違いがこのコードがコンパイルされる理由だと思いますか?


const T* t = f();
t = 0; // assignment to const?? - no, it is not the T* that is const, just the T.

コンパイラーと同じようにそれを読むことを学ぶと、特定の複合型が何であるかを理解しやすくなるだけでなく、必要なときに複合型をより簡単に宣言できるようになります。複合型によって私は次のようなことについて話している:

T const * const&

コンパイラが右から左、内側から外側へ読み取ることがわかっている場合、その意味は非常に明白になり、それを書く必要がある場合は、簡単に行うことができます。定数Tへの定数ポインタへの参照。次に、 「Tへの定数ポインターへのポインターへの参照」。単に左から右への表記を使用すると、これは非常に簡単だと思います。

要するに、最初は右から左への文法を常に必要とするときだけではなく(常にそうであるから)使用するのは不自然に思われますが、コード行が何を意味するかを覚えるのははるかに簡単です。そして、あなたが何を意味するかを書く方法。これは、宣言で「、」を許可しない理由と同じです。


T* ptr1 = 0, ptr2 = 0; // oops!!!


// do it this way please! T* ptr1 = 0; T* ptr2 = 0;

技術的にはすべて同じですが、さまざまな容量の人々を同じことに取り組んでいるときに、誰もが最も簡単に理解して使用できる方法を使用するようにする傾向があります。私の経験から、「T const&」がその方法であることがわかりました。

3
Edward Strange

個人的な好みだと思います。 2つのバリアントの間に違いはありません。

3

コードは主に英語ベースであるため、プログラマは左から右に読む傾向があるため、コンパイラは_const T&_が自然に読み取るように_T const&_が自然に読み取るようにします。 T)

3
Corey

私はconst T&の強力な擁護者でした。論理的に左から右に読みます(定数Tの参照です)。 (そして、私がその時点までに遭遇したほとんどのコードはそのように書かれていたので、おそらくいくつかのバイアスがあります。)

長年にわたり、他の人々がすでに回答している理由で少し物事に負担をかけるいくつかのコーナーケース(複数のポインター/参照レイヤー、複数の変数宣言、メソッドポインターなど)に遭遇しました。多くの場合、追加のtypedefを導入することで、これらのケースをある程度「解く」のに役立ちますが、一度しか使用されない場合でも、何かの名前を考え出す必要があります。

私にとっての転換点は、C++ 11でのautoの導入であり、特にrange-based-forと組み合わせた場合です。それで、私は反転して、代わりにT const&(または「定数Tへの参照」、右から左に読む)を強く好むようになりました。コンパイラが実際に解析する方法との整合性が高まるだけでなく、型をautoで置き換えると、常に左側に配置され、読みやすくなります。

比較:

for (const T& a : list)         for (T& a : list)
for (T const& a : list)         for (T& a : list)
for (const auto& a : list)      for (auto& a : list)
for (auto const& a : list)      for (auto& a : list)

同じの非constバージョンを追加した右側の列にも注意してください。少なくとも私にとって、const対non-constおよびauto対explicitはすべて、Tの後にconstが出現する場合に最も一貫しているように見えます。

しかし、これはスタイルの選択であり、絶対に正しい答えはありません。

1
Miral

これは、宣言を右から左に読むと役立つ場合があるためです。

char const*
const char*

どちらもconst charへのポインタです。

1
JRL

constおよび&のように遠くに離れます

krog::FlamBlott<std::map<HurkleKey,Spleen<int>>
speckleFlams( const krog::Floonage & floon, const std::map<krog::HackleSpoon,std::vector<krog::HeckleObservation<OBSN>>> & obsmap);

krog::FlamFinagleReport
finagleFlams( const krog::Floonage & floon, std::map<krog::HackleSpoon,std::vector<krog::HeckleObservation<OBSN>>> & obsmap)

...すると、最初の「obsmap」がconst参照であり、2番目はそうではないことが見落とされやすくなります。

0
greggo