web-dev-qa-db-ja.com

なぜvoidへの参照を持つことが不可能なのですか?

Voidへの参照を持つことが不可能なのはなぜですか?私がC++標準で見つけた唯一のものは、この行、8.3.2.1です。

cvvoidへの参照」タイプを指定する宣言子の形式が正しくありません。

なぜそうなのですか? void&を受け入れる「ジェネリック」関数を記述できないのはなぜですか?

明確にするために、私は、voidへの参照を使用する方がテンプレートを使用するよりも優れている可能性があるという有用なアプリケーションを念頭に置いていませんが、この構成を禁止する理由について興味があります。


少し明確にするために、voidへの参照を「そのまま」使用することは、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
}
42
Luc Touraille

Voidへの参照があった場合、それをどうしますか?数字でも、文字でも、ポインタでも、そのようなものでもありません。架空のジェネリック関数は、アドレス(サイズではなく)を取得する以外は、操作を実行できませんでした。

「void」には2つの用途があります。タイプの知識を放棄すること(void *のように)と、何かではなく何も指定しないこと(void関数の戻り値)です。どちらの場合も、アドレスがある場合を除いて、無効なものについて何も言うことはできません。

何かが役立つ方法を考えることができず、私ができない場合、それは少なくとも何かが役に立たないという証拠であり、それは少なくともここでの理論的根拠の一部である可能性があります。

38
David Thornley

最初に自分自身に尋ねてください、どのようにvoidポインタを逆参照しますか?

void *p = /*something*/ ;
cout << *p << endl;

上記のコードは無意味です。voidがある理由の1つは、「ここで一般的なポインター作業を行う必要があり、何を指しているのかわからない、または気にしない」と言えるためです。定義上、コンパイラはvoid *が何を指しているのかわからないため、逆参照することはできません。キャストすることでできますが、コンパイラーはできません。

ボイドへの参照にも同じ問題があります。定義上、ポイントされたデータには型がないため、意味のある方法で参照することはできません。

それを参照するには、プログラマーが別の型にキャストする必要があります。そうすれば、型付きの参照を持つことができます。

私がこれを私が望んでいたように説明したかどうかはわかりません。

ルーベン、何か考えはありますか?

編集:編集に回答します。

Void *データを渡す最初の関数を取ります。データは完全に有効なアイテムであり、それを使用して計算できます。または、ログを実装している場合は、ログに記録できます。

logger << data;

住所データポイントを取得します。データを逆参照しようとすると、コンパイラーによってエラーが発生します(現時点では、C++コンパイラーが手元にないため、実際のエラーはわかりません)。例えば.

void* data = /* some assignment */;
logger << *data; // compiler error.

これで、コンパイラは何らかの理由でvoid *を逆参照することを許可しません(意味がありません)。これは、参照であるためを除いて、void&dataへの参照を表します。常に暗黙的に逆参照されます。コンパイラーでは、1つの操作でvoid *を逆参照することはできません。また、常にそれを逆参照することはできません。

void& data = /* some assignment *.;
logger << data; // means same as logger << *data above

できません[〜#〜]何でも[〜#〜]データに[〜#〜]例外[〜#〜]アドレスを取得すると、それを行うために言語に組み込まれた完全に良い-そして安全な-方法、すなわち.

void* data;

これはもっと意味がありますか?

14
Binary Worrier

参照は、何かのインスタンスへの参照です。何かのインスタンスをvoid型にすることはできません。何かのインスタンスには、特定のタイプ(および場合によっては基本タイプ)が必要です。

6
ChrisW

OK、1つはこれについて私を悩ませています。上記のように、_void*_の考え方は、アドレスを含む有効な変数がまだあるが、タイプは無視されているということです。アドレスデータを引き続き処理できるため、これは許容できるようです。このコンテキストでは、タイプはやや不必要です(またはそれほど重要ではありません)。逆参照するのは悪いことです。なぜなら、試してメンバーにアクセスするは意味がないからです。 _p.mem_。参照するクラスがわからないため、ジャンプするメモリ、従うべきvtableポインタがわかりません。

ただし、pはオブジェクトのみを参照し、そのデータは参照しないため、それ自体で問題がないことは理にかなっているように思われます。そのためにクラス情報は必要ありません。アドレスだけが必要です。これはまったく役に立たないことは理解していますが、物事がいつ崩壊するかを定義する上で重要です。この概念を許可すると、C++参照(常に逆参照されますが、何にもアクセスしません)。 void& ref = static_cast< &void >(obj)も意味があるため、void参照を許可します。担当者に取り上げるべきだと言っているわけではありませんが、「理にかなっている」という観点からすると、正しいように思えますね。

Luc Tourailleが上で指摘したように(少なくともこれは私の解釈です)、実装することはできますが、問題は意味論的なものです。私がたどり着いた合理的な説明は、オブジェクト変数は一連のメモリの「タグ」であるため、型は重要な意味的価値があるということでした。したがって、ポインタは、アドレス値を持つ変数と見なされ、型を定義するための鍵ではなく、やや不必要なものとして扱います。

誰かがそれに同意しますか?

2
Andy

技術的に言えば、保証されるのは、オブジェクトへの参照がそのエイリアスであるということだけです。内部参照引数の受け渡しがポインターを使用して行われることは、実装の詳細です。これは、参照がaddress-ofでもある&演算子を再利用するために混乱する可能性がありますが、演算子は実際には異なるコンテキストで異なる意味を持っていることに注意してください(変数またはパラメーター宣言では、参照型を示します。それ以外の場合は、address-ofです。 、ビット単位の場合を除いて-and)。技術的にはオブジェクトの単なるエイリアスであるため、Worrierが説明したように、参照は「常に逆参照」されます。

2
Joseph Garvin

これは、言われたこと、そして私が考えたことの要約です。

Voidへの参照が許可されない2つの主な理由


1 まったく役に立たなかったでしょう。

実際、Cの時代を振り返ると、voidポインターには2つの目的がありました。

  • メモリ管理(例:malloc)
  • ジェネリック性(あらゆるタイプの引数を受け入れることができる関数の記述)

C++が登場したとき、テンプレートは汎用性を実装するための最良のソリューションになりました。ただし、カスタムメモリ管理は依然として可能である必要があり、C++とCの間の相互運用性が大きな懸念事項であったため、void *が維持されました。架空の無効参照はメモリ管理には役立たず、汎用性はすでにカバーされているため、基本的にはほとんど役に立ちません(以下で説明する非無効性の保証を除く)。

2 それでは何もできません

Voidポインターを使用する場合、それを逆参照することはできません。参照の場合に置き換えられます。つまり、(常に仮想の)void参照を使用することはできません。そう

void *data = // something
// using *data and data-> is forbidden

void &data = // something
// using data is forbidden

ただし、参照を「逆参照」する必要がない(このフレーズはひどく間違っていますが、私の主張は理解できます)が、そのアドレスのみを取得するユースケースを考えることができます。私が次の関数を持っていると仮定しましょう:

void foo(void *dataptr)
{
    assert(dataptr != NULL); // or != 0
    // do something with dataptr
}

この煩わしいアサートを回避するために、次のように関数を記述できます。

void foo(void &dataref)
{
    void *data = &dataref;
    // do something with data
}

ただし、これが機能するには、&datarefdataptrと同等である必要がありますそうではありません&dataref&*dataptrと同等です!

したがって、アドレスを取得することでさえ、少なくとも概念的には逆参照を意味します(舞台裏では、最初の同等性はおそらく真ですが、セマンティックレベルではそうではありません)。したがって、データを使用することはまったくできないため、ボイド参照は異常です。

2
Luc Touraille

参照は、参照解除されたポインターと考えることができます。構文的には、参照をポインターではないかのように扱います。逆参照するために*演算子は必要なく、を使用できます。 ->ではなく、そのメンバーにアクセスします。

ただし、voidポインタを逆参照することはできません。 Binary Worrierが指摘しているように、これを実行しようとすると、コンパイラエラーが発生します。また、逆参照されたvoidポインターを使用できない場合は、void参照を使用できないことを意味します。

0
Dima

もしそうなら、それらは意味的にポインターから区別されず、構文糖衣になります。ある参考文献には、「私はこのタイプの何かを指している」と書かれています。 voidまたはnull参照を許可すると、ポインターとの違いが弱まります。

確かに、参照がもう存在しないオブジェクトを参照することは可能ですが、それは例外です。

0
JohnMcG

以下はnot無効参照の概念の防御です。私はそれを野生からの逸話として提供します。変なにおいがしないか自問してみてください。

私の会社は、C++を商業的に使用した最初の会社のひとつであり、最初は 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バインディングを持つ型付き参照があります!一瞬の考えは、おそらくこの特定のイディオムのより良い代替案を示唆するはずです...

0
Don Wakefield