49

void への参照ができないのはなぜですか? C++ 標準で見つけたのは、この行の8.3.2.1 だけです。

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

なぜそのようになっているのですか?を受け入れる「汎用」関数を作成できないのはなぜvoid&ですか?

明確にするために、voidへの参照を使用する方がテンプレートを使用するよりも優れている可能性がある有用なアプリケーションは考えていませんが、この構成を禁止する理由について知りたいだけです。


少し明確にするために、ボイドへの参照を「そのまま」使用することは、ボイドへのポインタを逆参照するのと同じくらい無意味であることを理解しています。しかし、それを使用するために何らかの型への参照にキャストすることはできますよね? 実際、次のスニペットが機能する理由がわかりません...

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
}
4

10 に答える 10

39

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

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

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

于 2009-01-19T15:26:16.957 に答える
15

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

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

上記のコードは無意味です。無効になっている理由の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;

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

于 2009-01-19T15:18:09.937 に答える
6

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

于 2009-01-19T15:15:31.440 に答える
3

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

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
}

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

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

于 2009-01-19T17:38:05.553 に答える
2

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

于 2009-01-19T16:16:41.800 に答える
2

OK、これについて私を悩ませていることが 1 つあります。上記のように、 の考え方はvoid*、アドレスを含む有効な変数がまだあるということですが、型は無視されています。住所データを扱うことができるので、これは許容できるように思われます。このコンテキストでは、タイプは多少不必要です (またはあまり重要ではありません)。メンバーにアクセスしようとしても意味がないため、逆参照は悪いことですp.mem。どのクラスを参照すればよいか、したがってジャンプ先のメモリ、従うべき vtable ポインタがわからない。

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

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

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

于 2009-04-20T01:08:59.583 に答える
0

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

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

于 2009-01-19T17:06:42.697 に答える
0

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

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

于 2009-01-19T18:39:07.380 に答える
0

以下は、無効参照の概念を擁護するものではありません。野生の逸話として提供します。変なにおいがしないかどうか自問してください。

私の会社は、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バインディングの可能性がある型付き参照です! ちょっと考えてみれば、おそらくこの特定のイディオムのより良い代替案が見つかるはずです...

于 2009-04-20T02:51:37.277 に答える
-3

voidは、定義上、存在しないものであるため、そのアドレスを持つことは論理的ではありません。

于 2009-01-19T15:16:39.050 に答える