なぜボイドへの参照を持つことが不可能なのですか?
-
19-08-2019 - |
質問
なぜvoidを参照できないのですか? C ++標準で見つけた唯一のことは、 8.3.2.1
のこの行です型を指定する宣言子<!> quot; cv への参照void <!> quot;不正な形式です。
なぜそうなのですか?なぜ<!> quot; generic <!> quot;を書けないのですか? void&
を受け入れる関数?
明確にするために、ボイドへの参照を使用する方がテンプレートを使用するよりも優れている可能性のある有用なアプリケーションはありませんが、この構成を禁止する理由に興味があります。
少し明確にするために、voidへの参照<!> quot; as << >> quot;を使用することを理解しています。 voidへのポインターを逆参照するのと同じくらい無意味です。ただし、それを使用するために sometype への参照にキャストすることはできませんか?実際、次のスニペットが機能する理由はわかりません...
void foo(void *data)
{
int *i = reinterpret_cast<int*>(data);
// do something with i
}
...これは次のことができません:
void foo(void &data)
{
int &i = reinterpret_cast<int&>(data);
// do something with i
}
解決
voidへの参照があった場合、それをどうしますか?それは数字でも、文字でも、ポインターでも、そのようなものでもありません。仮のジェネリック関数は、アドレス(サイズではなく)を取得することを除いて、操作を実行できませんでした。
<!> quot; void <!> quot;型の知識を放棄する(void *のように)、および何かとは対照的に何も指定しない(void関数の戻り)という2つの用途があります。どちらの場合も、アドレスを持っている可能性があることを除いて、無効なものについて何かを言うことはできません。
何か有用な方法が考えられないなら、私にはできません。それは、少なくとも何かが役に立たないという証拠であり、少なくともここの理論的根拠の一部である可能性があります。
他のヒント
最初に自分自身に質問し、ボイドポインターをどのように逆参照しますか?
void *p = /*something*/ ;
cout << *p << endl;
上記のコードは無意味です、無効になっている理由の1つは、ここで<!> quot;と言うことができるためです。ここで一般的なポインターの作業を行う必要があります。 > quot;。 定義上、コンパイラはvoid *が何を指しているのかを知らないため、逆参照することはできません。キャストすることでできますが、コンパイラーはできません。
voidへの参照も同じ問題に悩まされます。定義により、指すデータには型がないため、意味のある方法で参照することはできません。
それを参照するには、プログラマーが別の型にキャストする必要があります。その後、型付き参照を使用できます。
私がこれを説明したかどうか、私が望んでいたかどうかわからない。
ルーベン、何か考えはありますか?
編集:編集に回答します。
void *データを渡す最初の関数を取得します。 データは完全に有効なアイテムであり、それを使用して計算できます。または、ロギングを実装している場合は、ログに記録できます。
logger << data;
そしてアドレスデータポイントを取得します。データを逆参照しようとすると、コンパイラーはエラーを表示します(現時点ではC ++コンパイラーが手元にないので、実際のエラーは不明です)。 例えば
void* data = /* some assignment */;
logger << *data; // compiler error.
現在、コンパイラは何らかの理由でvoid *の逆参照を許可しません(意味がありません)。これは、参照<であるためを除いて、void <!> amp; dataへの参照を表しますem>常に暗黙的に間接参照されます。コンパイラーは、1つの操作でvoid *を間接参照することはできません。常に間接参照することはできません。
void& data = /* some assignment *.;
logger << data; // means same as logger << *data above
データに対して何でもを実行することはできません EXCEPT アドレスを取得します/ p>
void* data;
これはもう意味がありますか?
参照は、何かのインスタンスへの参照です。
何かのインスタンスは、タイプvoid
にはできません。
何かのインスタンスには、特定のタイプ(および場合によってはベースタイプ)が必要です。
これまでに述べられてきた、私が考えてきたさまざまなことの要約です。
voidへの参照が許可されない2つの主な理由
1 これらはまったく役に立たなかったでしょう。
実際、Cの時代を振り返ると、voidポインターには2つの目的がありました。
- メモリ管理(mallocなど)
- Genericity(あらゆるタイプの引数を受け入れることができる関数を書く)
C ++が登場したとき、テンプレートは汎用性を実装するための最良のソリューションになりました。ただし、カスタムメモリ管理は依然として可能でなければならず、C ++とCの相互運用性が大きな懸念事項であったため、void *が維持されました。仮想のvoid参照はメモリ管理には役に立たず、汎用性はすでにカバーされているため、基本的にはほとんど役に立ちません(以下で説明する非nullnessの保証を除く)。
2 これで何もできない
voidポインターを使用する場合、それを間接参照することはできません。参照の場合に置き換えられるため、(常に仮定の)void参照を使用できません。だから
void *data = // something
// using *data and data-> is forbidden
void &data = // something
// using data is forbidden
ただし、参照が<!> quot; dereferenced <!> quot;である必要がないユースケースを考えることができます。 (このフレーズは非常に間違っていますが、あなたは私のポイントを得ます)、しかし、我々はそのアドレスだけを取るでしょう。次の機能があると仮定しましょう:
void foo(void *dataptr)
{
assert(dataptr != NULL); // or != 0
// do something with dataptr
}
このうっとうしい主張を避けるために、このように関数を書くことができます:
void foo(void &dataref)
{
void *data = &dataref;
// do something with data
}
ただし、これが機能するには、&dataref
がdataptr
と同等である必要があり、そうではありません:&*dataptr
は<=>と同等です!
したがって、アドレスを取得することでさえ、少なくとも概念的には逆参照を意味します(舞台裏では、最初の同等性はおそらく真実ですが、意味レベルではそうではありません)。したがって、データを使用することはまったくできないため、void参照は異常です。
技術的に言えば、オブジェクトへの参照がそのエイリアスであることだけが保証されています。フード参照引数の受け渡しは、ポインターを使用して行われることは、実装の詳細です。 <!> ampを再利用する参照のため、これは混乱を招く可能性があります。演算子もアドレスオブですが、演算子は実際には異なるコンテキストで異なる意味を持っていることに注意してください(変数またはパラメーター宣言では、参照型を示します。技術的には単なるオブジェクトのエイリアスであるため、Worrierが説明したように、参照は「常に間接参照」されます。
OK、これについて私を悩ませています。上記のように、void*
の考え方は、アドレスを含む有効な変数がまだあるということですが、型は無視されます。住所データを引き続き使用できるため、これは許容できるようです。このコンテキストでは、型はいくぶん余分です(またはそれほど重要ではありません)。参照しようとすると、メンバーにアクセスすることは意味をなさないので、それを参照するのは悪いことです。 p.mem
。参照するクラス、したがって、ジャンプするメモリ、従うべきvtableポインターがわかりません。
しかし、オブジェクトを参照するだけで、データはまったく参照しないため、p
だけでも問題ないように思えます。そのためにクラス情報は必要なく、住所だけが必要です。これは絶対に役に立たないことは理解していますが、事態がいつ崩壊するかを定義する上で重要です。この概念を許可し、C ++参照(常に間接参照されますが、何にもアクセスしません) void& ref = static_cast< &void >(obj)
も理にかなっているため、void参照が許可されます。誰でも担当者に取り上げるべきだと言っているわけではありませんが、<!> quot;意味をなす<!> quot;視点、それは正しいように思えます、いいえ?
Luc Tourailleが上記で指摘したように(少なくとも、これは私の解釈です)、実装することはできましたが、問題はセマンティックなものです。私が思いつく合理的な説明は、オブジェクト変数は<!> quot; tag <!> quot;一連のメモリの場合、タイプは重要なセマンティック値です。したがって、ポインターは、アドレス値を持つ変数と見なされ、型をやや不必要なものとして扱います-定義するためのキーではありません。
誰もがそれに同意しますか?
参照は、参照解除されたポインターと考えることができます。構文的には、参照をポインタではないものとして扱います。参照を逆参照するために*演算子は必要なく、を使用できます。 -<!> gt;ではなくメンバーにアクセスします。
ただし、void
ポインターを逆参照することはできません。 Binary Worrierがそれをしようとすると指摘されたように、コンパイラエラーが発生します。また、参照解除されたvoidポインターを取得できない場合、それはvoid参照を取得できないことを意味します。
もしそうであれば、それらは意味的にポインタと区別されず、構文上の砂糖になります。参照では、<!> quot;このタイプの何かを参照しています。<!> quot; voidまたはnull参照を許可すると、ポインターとの違いが弱まります。
確かに、参照がもう存在しないオブジェクトを参照することは可能ですが、それは例外です。
以下は、無効な参照の概念の防御ではありません 。野生からの逸話として提供します。変な匂いがしないかどうか自問してください。
私の会社はC ++を最初に商用で使用した会社の1つで、最初は Cfront を使用してコンパイルしました。初期の開発者はまだ言語を学んでおり、一般的に本のすべてのトリックを使用していました(どこでも演算子です!)。以下は、彼らがクールだと思ったトリックです:
void Foo::something(int action, ostream &os = *(ostream *)0)
{
ostream *os_p = &os;
if (&os == (ostream *)0) {
os_p = &cerr;
}
// continue with method
}
つまり、ここにはvoid参照ではなく、潜在的に void バインディングを持つ型付き参照があります!ちょっと考えてみると、おそらくこの特定のイディオムのより良い代替案を示唆しているはずです...
void は、定義上、存在しないものです。そのため、アドレスを持つことは論理的ではありません。