これはC ++の癖/機能です。参照を型とは考えていませんが、実際には型システムに「座っています」。これは厄介に思えますが(参照が使用されると、参照のセマンティクスが自動的に発生し、参照が「邪魔にならない」)、参照が外部の個別の属性としてではなく型システムでモデル化される理由がいくつかあります。タイプ。
まず、宣言された名前のすべての属性が型システムに含まれている必要はないことを考えてみましょう。C言語から、「ストレージクラス」と「リンケージ」があります。名前はとして導入できますextern const int ri
。ここで、extern
は静的ストレージクラスとリンケージの存在を示します。タイプはちょうどconst int
です。
C ++は明らかに、式には型システムの外部にある属性があるという概念を取り入れています。この言語には、式が示すことができる非型属性の増加する数を整理する試みである「値クラス」の概念があります。
しかし、参照はタイプです。どうして?
以前は、C ++チュートリアルで、宣言はタイプを持っているものとしてconst int &ri
導入さri
れたがconst int
、セマンティクスを参照していると説明されていました。その参照セマンティクスはタイプではありませんでした。それは単に名前と保管場所の間の異常な関係を示す一種の属性でした。さらに、参照が型ではないという事実は、型構築構文で許可されていても、参照に基づいて型を構築できない理由を合理化するために使用されました。たとえば、配列または参照へのポインタは使用できません:const int &ari[5]
およびconst int &*pri
。
しかし実際には、参照は型であるため、decltype(ri)
修飾されていない参照型ノードを取得します。を使用して基になる型に到達するには、型ツリーでこのノードを通過する必要がありますremove_reference
。
を使用するri
と、参照は透過的に解決されるため、ri
「見た目も使い心地もi
」、その「エイリアス」と呼ぶことができます。ただし、型システムでri
は、実際には「参照 」である型がありconst int
ます。
なぜ参照型なのですか?
参照が型ではない場合、これらの関数は同じ型であると見なされることを考慮してください。
void foo(int);
void foo(int &);
それは、ほとんど自明の理由であるはずがありません。それらが同じタイプである場合、それはどちらかの宣言がどちらの定義にも適していることを意味するため、すべての(int)
関数が参照を取得している疑いがあります。
同様に、参照が型でない場合、これら2つのクラス宣言は同等になります。
class foo {
int m;
};
class foo {
int &m;
};
1つの翻訳ユニットが1つの宣言を使用し、同じプログラム内の別の翻訳ユニットが他の宣言を使用するのは正しいことです。
事実、参照は実装の違いを意味し、 C ++の型はエンティティの実装、いわばビット単位の「レイアウト」に関係しているため、それを型から分離することは不可能です。2つの関数のタイプが同じである場合、それらは同じバイナリ呼び出し規約で呼び出すことができます。ABIは同じです。2つの構造体またはクラスが同じタイプである場合、それらのレイアウトは同じであり、すべてのメンバーへのアクセスのセマンティクスも同じです。参照の存在は型のこれらの側面を変えるので、それらを型システムに組み込むことは簡単な設計上の決定です。(ただし、ここで反論に注意してください。構造体/クラスのメンバーはである可能性がありstatic
、これによって表現も変更されますが、タイプではありません!)
したがって、参照は「二級市民」として型システムにあります(ISO Cの関数や配列とは異なります)。参照へのポインタやそれらの配列を宣言するなど、参照で「実行」できない特定のことがあります。しかし、それはそれらがタイプではないという意味ではありません。それらは、それが理にかなっている方法でタイプされていないだけです。
これらすべての第2クラスの制限が必須というわけではありません。参照の構造があるとすると、参照の配列が存在する可能性があります。例えば
int x = 0, y = 0;
int &ar[2] = { x, y };
これはC ++で実装されていないだけで、それだけです。ただし、参照から持ち上げられたポインタは参照されたオブジェクトに移動するだけなので、参照へのポインタはまったく意味がありません。参照の配列がない理由として考えられるのは、C ++の人々が、配列をCから継承された一種の低レベルの機能であり、修復不可能な多くの方法で壊れていると考えており、配列に触れたくないためです。新しいものの基礎。ただし、参照の配列が存在することで、参照が型である必要があることを明確に示すことができます。
非const
-qualifiable種類:あまりにも、ISO C90で見られます!
いくつかの回答は、参照がconst
修飾子をとらないという事実をほのめかしています。宣言const int &ri = i
は修飾された参照を作成しようとさえしていないので、それはむしろ赤いニシンconst
です:それはconst修飾された型への参照です(それ自体はそうではありませんconst
)。const in *ri
何かへのポインタを宣言するのと同じですconst
が、そのポインタ自体はそうではありませんconst
。
とは言うものの、参照がconst
修飾子自体を運ぶことができないのは事実です。
しかし、これはそれほど奇妙なことではありません。ISO C 90言語でも、すべてのタイプがであるとは限りませんconst
。つまり、配列はできません。
まず、const配列を宣言するための構文が存在しません:int a const [42]
エラーです。
ただし、上記の宣言が実行しようとしていることは、中間を介して表現できますtypedef
。
typedef int array_t[42];
const array_t a;
しかし、これは見た目どおりには機能しません。この宣言では、a
どちらがconst
修飾されるかではなく、要素です!つまり、a[0]
あるconst int
が、a
単に「int型の配列」です。したがって、これには診断は必要ありません。
int *p = a;
これは行います:
a[0] = 1;
繰り返しますが、これは、参照がある意味で、配列のように型システムの「第2クラス」であるという考えを強調しています。
配列にも参照のように「目に見えない変換動作」があるため、類推がさらに深く当てはまることに注意してください。プログラマーが明示的な演算子を使用しなくても、式が使用されたかのように、識別子はa
自動的にint *
ポインターに変わります&a[0]
。これは、参照をri
一次式として使用するときに、参照i
がバインドされているオブジェクトを魔法のように示す方法に似ています。これは、「配列からポインタへの減衰」のような別の「減衰」です。
また、「配列からポインタへ」が「配列はCおよびC ++の単なるポインタである」と誤って考えてしまうことに混乱してはならないのと同じように、参照が独自のタイプを持たない単なるエイリアスであると考えてはなりません。
場合decltype(ri)
、その参照先オブジェクトへの参照の通常の変換を抑制し、これは大差ないsizeof a
アレイ・ツー・ポインタ変換を抑制し、そして上で動作するアレイ型自体のサイズを計算します。
boolalpha(cout)
非常に珍しいです。std::cout << boolalpha
代わりに行うことができます。