Why is it impossible to have a reference-to-void?

橙三吉。 提交于 2019-12-03 04:32:46

问题


Why is it impossible to have a reference to void? The only thing I found in the C++ Standard is this line, at 8.3.2.1

A declarator that specifies the type "reference to cv void" is ill-formed.

Why is it that way? Why can't I write a "generic" function that accept a void&?

Just to be clear, I have no useful application in mind where using a reference-to-void could be better than using templates, but I'm just curious about the rationale for forbidding this construct.


To clarify a little, I understand that using a reference-to-void "as is" would be as meaningless as dereferencing a pointer-to-void. However, I could cast it to a reference-to-sometype in order to use it, couldn't I? In fact, I don't see why the following snippet can work...

void foo(void *data)
{
    int *i = reinterpret_cast<int*>(data);
    // do something with i
}

...while this one cannot:

void foo(void &data)
{
    int &i = reinterpret_cast<int&>(data);
    // do something with i
}

回答1:


If you did have a reference to void, what would you do with it? It wouldn't be a number, or a character, or a pointer, or anything like that. Your hypothetical generic function couldn't perform any operation on it, except taking its address (and not its size).

"void" has two uses: to disclaim any knowledge of type (as in void *), and to specify nothing as opposed to something (void function return). In neither case is it possible to say anything about a void something except that it may have an address.

If you can't think of a way something can be useful, and I can't, that is at least evidence that something is useless, and that may well be at least part of the rationale here.




回答2:


Ask your self first, how you would de-reference a void pointer?

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

The above code is meaningless, one of the reasons we have void is so we can say "I need to do some generic pointer work here, and I neither know nor care what I'm pointing to". By definition, the compiler doesn't know what a void * points to, therefore it can't dereference it. You can - by casting - but the compiler can't.

A reference to a void sufferes from the same problem, by definition the data pointed to doesn't have a type, therefore it can't be referenced in any meaningful way.

To reference it you - the programmer - need to cast it to another type, then you can have a typed reference to it.

Not sure if I explained this as well as I wanted to.

Ruben, any thoughts?

EDIT: To answer your edit.

Take the first function, where you pass void* data. data is a perfectly valid item, you can compute with it, or if you've some logging implemented, you can log it.

logger << data;

and you'll get the address data points to. If you try to dereference data, the compiler will give you an error (don't have C++ compiler handy at moment, so not sure of the actual error). e.g.

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

Now, the compiler won't let you dereference a void* for any reason (it doesn't make sense), the same stands for a reference to void &data, except that because it's a reference it's implicitly dereferenced all the time. The compiler won't let you dereference a void* on one operation, it's not going to let you dereference it constantly.

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

You can't do ANYTHING to data EXCEPT take it's address, and there's a perfectly good - and safe - method built into the languge to do that, i.e.

void* data;

Is this making any more sense?




回答3:


A reference is a reference to an instance of something. An instance of something can't be of type void. Any instance of something must have a specific type (and possibly base types).




回答4:


Here's a summary of the different things that have been said, and that I've thought of.

Two main reasons why reference-to-void are disallowed


1 They would have been totally useless.

Indeed, if we look back at the times of C, void pointers had two purposes:

  • Memory management (e.g. malloc)
  • Genericity (writing functions that can accept any type of arguments)

When C++ came out, templates became the best solution to implement genericity. However, custom memory management still had to be possible, and interoperability between C++ and C was a major concern, so void* was kept. An hypothetical void reference would be of no help with memory management, and genericity is already covered, so basically it would have almost no use (except for the guarantee of non-nullness described below).

2 You wouldn't be able to do anything with it

When using a void pointer, you're not allowed to dereference it; transposed to the case of references, that means you can't use the (always hypothetical) void reference. So

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

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

However, we could think of a use case where the reference wouldn't have to be "dereferenced" (this phrase is awfully incorrect, but you get my point), but where we would only take its address . Let's assume I have the following function:

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

To avoid this annoying assert, I could write the function this way:

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

However, for this to work, &dataref needs to be equivalent to dataptr, which is not the case: &dataref is equivalent to &*dataptr!

Therefore, even taking the address implies a dereferencing, at least conceptually (behind the scenes, the first equivalence is probably true, but at the semantic level it is not). Consequently, there is absolutely no usage we can make of data, so void references are an aberration.




回答5:


Technically speaking, all that is guaranteed is that a reference to an object is an alias for it. That under the hood reference argument passing is done with pointers is an implementation detail. This can be confusing because of references reusing the & operator which is also address-of, but keep in mind that the operator actually has different meanings in different contexts (in a variable or parameter declaration it denotes a reference type, otherwise it's address-of, except when it's bitwise-and). Because it's technically just an alias for an object, a reference is 'always dereferenced' as Worrier explained.




回答6:


OK, one thing is bugging me about this. The idea of a void*, as mentioned above, is that you still have a valid variable containing an address, but the type is being ignored. This seems allowable since we can still work with the address data - the type is somewhat superfluous (or less important) in this context. Dereferencing it is bad, because to try and access a member doesn't make sense e.g. p.mem. We don't know what class to refer to, and thus the memory to jump to, the vtable pointers to follow.

However, it'd then seem to make sense that p on its own would be OK since it'd only refer to the object, but none of its data. No class information is needed to do so, just the address. I understand there's absolutely no use for this, but it's important in defining when things break down. Allowing this notion, a C++ reference (constantly dereferenced but not accessing anything) e.g. void& ref = static_cast< &void >(obj) also makes sense, and thus would allow void references. I'm not saying anyone should take it up with those in charge, but from a "making sense" point of view, it'd seem correct, no?

As Luc Touraille pointed out above (at least, this is my interpretation), it could be implemented, but the issue is a semantic one. The reasonable explanation I could come to was that since an object variable is a "tag" for a sequence of memory, the type is of important semantic value. Thus, the pointer, being thought of as a variable with an address value, treats the type as somewhat superfluous - not key to defining it.

Would anyone agree with that?




回答7:


You can think of a reference as a de-referenced pointer. Syntactically you treat a reference as though it is not a pointer: you do not need the * operator to dereference it, and you can use . rather than -> to access its members.

However, you cannot dereference a void pointer. As pointed out by Binary Worrier trying to do that will give you a compiler error. And if you cannot have a dereferenced void pointer, that means you cannot have a void reference.




回答8:


If they were, they would be semantically non-differentiated from pointers, and would amount to syntactic sugar. A reference says, "I refer to something that is of this type." Allowing void or null reference would weaken that difference from pointers.

Granted, it's still possible for a reference to refer to an object that doesn't exist anymore, but that is an exception.




回答9:


The following is not a defense of the notion of void references. I offer it as an anecdote from the wild. Ask yourself if it doesn't smell funny.

My company was one of the first using C++ commercially, and initially compiled using Cfront. Early developers were still learning the language, and generally using every trick in the book (operators everywhere!). Here is a trick they thought was cool:

void Foo::something(int action, ostream &os = *(ostream *)0)
{
   ostream *os_p = &os;
   if (&os == (ostream *)0) {
      os_p = &cerr;
   }
   // continue with method
}

So here you have, not a void reference, but rather a typed reference with a potentially void binding! A moment's thought should probably suggest better alternatives to this particular idiom...




回答10:


void is something that, by definition, doesn't exist, so it is not logical to have it's adress.



来源:https://stackoverflow.com/questions/457896/why-is-it-impossible-to-have-a-reference-to-void

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!