53

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
}
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137

10 Answers10

36

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.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
  • 5
    Well there IS actually something that you can do with a void reference, it's just something about syntax by the way: you can pass the object without dereferencing it with &, it's stupid but I don't like use a lot of & around, I found more clear using refs – Francesco Belladonna Dec 22 '10 at 04:24
  • One use I would have is an `enforce_dlopen` function, which is guaranteed to not return a NULL-pointer (unlike `dlopen`). Unfortunately I can't express that fact in the function declaration. – Sebastian Mach Apr 23 '13 at 16:23
  • 36
    I think there is a use case for void references: unlike a void pointer (which can be a `nullptr`), a void reference would give you a guarantee that the reference actually refers to something, even if you cannot offer a static guarantee regarding what its type is. – isekaijin Jun 27 '13 at 10:11
  • I actually have a case I could use this ;) But I was anyway gogling for it with the expectation of it probably beeing not possible, because there is no sense in it (almost). – dhein Jun 09 '16 at 13:05
  • 6
    reference to void has a useful property of being used as a generic reference argument in a constructor. So you can have Foo a(anyReference) and it could internally convert it to a void pointer. It in turn allows you to pass references and the constructor can take care of casting it to a pointer for you. Sure it's far from idiomatic C++, but C++ is a multiparadigm language and things deemed dangerous in one place are a driving force in another. And as noted before, taking a void reference in a constructor is safer than taking a void pointer in a constructor. – Dmytro Dec 07 '16 at 18:41
  • 5
    If `void&` is useless there's no reason to forbid it. The standard is opinionated and introduces an unnecessary exception. – n.caillou Jan 16 '17 at 18:28
  • 10
    I don't see how this answers the question. How does OP's hypothetical function not work? Essentially it would provide the same functionality as `void*` but with the added certainty that it is not null – Rufus Jun 01 '17 at 10:04
  • There is definitely something useful you can do with it other than taking its address: you can pass it to dynamic_cast to cast it to a more useful type: `MyType& foo = dynamic_cast(voidRef);` That's the equivalent of what you could do with a void pointer, but you know the void reference isn't null (because it's a reference) and if you know that the void reference is supposed to be of MyType, you also know the dynamic_cast will throw an exception if your attempted typecast is illegal (a dynamic_cast of a pointer would just return null which you would then have to check for). – Some Guy Aug 10 '22 at 20:38
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?

Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
  • 1
    Yes, I agree with it. I think it's the most correct answer in this question. Void means "type is unknown" not "points to nothing". "Points to nothing" is null, not void. You can't create an instance of void because its size is unknown, but you can have a pointer to void because even though its type (and hence its size) is unknown, its address can still be known. Having a reference to void could still be meaningful, although certain operations like sizeof() would have to be undefined or return some default answer like "1" (since C/C++ doesn't allow objects to have zero size). – Some Guy Aug 11 '22 at 11:09
  • this answer is correct and valid – Rainb Sep 25 '22 at 20:47
  • I would like to take a `void& something` and get it "dynamic cast" to something else. So, a void reference could retain the `typeid`. I would be like if every virtual class descended from `void`. – André Caldas Apr 07 '23 at 12:01
4

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).

ChrisW
  • 54,973
  • 13
  • 116
  • 224
  • Although your statements are true, they are also irrelevant to the question being asked, because the type of a reference does not need to be the same as the type of the thing it refers to. For instance, in "`BaseClass& baseRef = derivedClassInstance;`", a reference with one type (a base class) refers to an instance of another type (a derived class). Having void references exist would not imply that the thing they are referencing also has type 'void'. This certainly is not the case with void pointers and the things they point to. – Some Guy Aug 12 '22 at 12:39
  • You wrote, "*a reference does not need to be the same as the type of the thing it refers to ... a reference with one type (a base class) refers to an instance of another type (a derived class)*" -- but I disagree, in that an instance of the derived type "**IS**" also an instance of the base type. https://stackoverflow.com/a/36162730/49942 – ChrisW Aug 12 '22 at 12:51
  • What about "`long x; char& y = *(char*)&x;`" then? In general, a reference simply means that the thing the reference references should be accessed "as if" it were of the reference's type. This is not a guarantee that what is referenced actually IS the given type. Just that the compiler is allowed to assume it is when dereferencing it to access its memory. This is the same as how types of pointers (versus the types of the things they point to) work when dereferencing. – Some Guy Aug 12 '22 at 13:15
  • Consider that other types essentially act as if they are derived types of 'void': like normal class inheritance, the derived types add additional operations to their "base type" void, and a void pointer can point to anything without a typecast, and can be downcast to a more type-specific pointer if you want to do something with the thing it points to. There is no reason references to void couldn't have been treated the same way. Obviously you can't have an instance of void, but you could have a void reference that references an instance of something else, and it could be downcast similarly. – Some Guy Aug 12 '22 at 13:19
  • Where do you ever even use `void*` except in (deprecated) C-style memory functions like `malloc` and `free`? Users -- i.e. any code which has or which receives a reference -- generally want to know what (type) something is; and being able to declare opaque (forward-defined) types, and template types, gives other options that are type-safer than `void*`. Also you're thinking of references as if they were a type of pointer, but I'm not sure that was their purpose. – ChrisW Aug 12 '22 at 19:08
2

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.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
  • "`&dataref` _is equivalent to_ `&*dataptr`" I am not sure I get you; do you mean here that `&dataref` is an rvalue, unlike `dataref`? – curiousguy Nov 22 '11 at 00:53
  • @curiousguy: I meant that since a reference can be seen (like [Binary Worrier noted](http://stackoverflow.com/q/457930/20984)) as a constantly dereferenced pointer, taking the adress of a reference is different from having the adress in the first place: conceptually, it is more like dereferencing an adress and taking the adress of the result, which implies a dereference, which is meaningless when dealing with `void` pointers. – Luc Touraille Nov 22 '11 at 08:35
  • 1
    This is not true: taking the address of something, including a reference, does not imply dereferencing at all as this answer claims. That's not how C++ works. A reference may be "seen as" as a constantly dereferenced pointer for conceptual purposes, but that does not mean it actually is one. If I have "int x; int& intref=x" and I then take "int* intPtr=&intref" I have not dereferenced anything. I have just taken the address of whatever lvalue the reference was referencing ("x"). Dereferencing means actually accessing the memory the reference/pointer points to ("x"), which has not happened. – Some Guy Aug 11 '22 at 10:51
2

Here are my five cents.

Each C++ expression is characterized by two independent properties: A type and a value category.

Suppose the code in the question is valid

void foo(void &data)
{
   //... 
}

Now one may ask the question, what is the type and the value category of the expression data? Naturally, it should be a lvalue of void type. Now, how about (void)5? Is it a prvalue of void type? How about std::move(data)? Should prvalue of void and xvalue of void be treated in fundamentally different ways? How about forming rvalue reference to void? This list just goes on and on.

In the case of pointer to void, none of these questions arise because if p is a pointer to void, then *p is not a valid expression according to the standard, and we don't need to worry about the type and value categories of *p. In the case of void reference, the corresponding statement has to be that the expression data itself is invalid, but if the expression itself is invalid, how can it be used in any reasonable way at all?

Weijun Zhou
  • 746
  • 1
  • 7
  • 25
1

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.

Joseph Garvin
  • 20,727
  • 18
  • 94
  • 165
  • This is incorrect. Although a reference is an alias, as you say, and doesn't have to be done using pointers, as you say, taking the address of the reference is defined to mean taking the address of the thing it references (because the reference is an alias, so what else could it be?) which basically means you get a pointer to the original object as if the reference alias hadn't been there. A reference is "automatically dereferenced" in many contexts, but it is not "always dereferenced" and taking its address is a context in which the reference is definitely not dereferenced at all. – Some Guy Aug 11 '22 at 11:13
  • @SomeGuy Not sure which part you think is incorrect. I'm referring to the syntax for declaring references using &, which overlaps with the prior established use of & for taking addresses. I'm not saying there's any ambiguity about what applying & to an already existing reference does. – Joseph Garvin Aug 11 '22 at 16:47
  • @JosephGavin OK, I see the point you were trying to make, and I agree that the similarity in syntax could be a cause of confusion. Still, I think that the phrase "always dereferenced" is inaccurate and is more confusing than helpful: Even referring to an lvalue by name with no aliases involved doesn't necessarily dereference it. It depends on the context. In "`int foo; int* bar=&foo;`" foo has not been dereferenced (its memory has not been accessed). Only its address has been taken. Adding an alias in the middle ("`int foo; int& bar = foo; int* baz=&bar;`") doesn't change this. – Some Guy Aug 12 '22 at 12:26
0

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.

Dima
  • 38,860
  • 14
  • 75
  • 115
0

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.

JohnMcG
  • 8,709
  • 6
  • 42
  • 49
  • Actually, a reference says "I refer to something which should be treated as if it were this type when accessed via this reference", which is basically how pointers also work. For instance, consider "`BaseClass& baseRef = derivedClassInstance;`" where the type of the reference is not the same as the type of the object it references. References don't differ from pointers in the way you describe, and really they DO amount to syntactic sugar, except for the fact that references are required to be non-null and not reassignable, both of which are important for avoiding bugs. – Some Guy Aug 12 '22 at 12:57
0

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...

Don Wakefield
  • 8,693
  • 3
  • 36
  • 54
  • 1
    You seem to be confusing null with void. Null (represented by 0 in this example) is a value. Void is a type. They are two different kinds of things, like '42' vs. 'int'. Regardless, [the C++ standard considers null references to be undefined behavior](https://stackoverflow.com/questions/4364536/is-null-reference-possible), and a conforming compiler could do anything it likes, [causing demons to fly out of your nose](http://www.catb.org/jargon/html/N/nasal-demons.html), or more likely, optimizing the 'if' statement in your example away completely because references aren't supposed to be null. – Some Guy Aug 12 '22 at 12:49
-5

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

dmajkic
  • 3,448
  • 1
  • 18
  • 24
  • 2
    doesn't plea for the existance of void* then, does it? – xtofl Jan 19 '09 at 15:17
  • 3
    I think you are confusing the *value* void with the *type* void (as in void* ). The title of the post didn't make this distinction either. – Andy Brice Jan 19 '09 at 15:58