void
is a bizarre wart in the C++ type system. It's an incomplete type that cannot be completed, and it has all sort of magic rules about the restricted ways it can be employed:
A type cv
void
is an incomplete type that cannot be completed; such a type has an empty set of values. It is used as the return type for functions that do not return a value. Any expression can be explicitly converted to type cvvoid
([expr.cast]). An expression of type cvvoid
shall be used only as an expression statement, as an operand of a comma expression, as a second or third operand of?:
([expr.cond]), as the operand oftypeid
,noexcept
, ordecltype
, as the expression in areturn
statement for a function with the return type cvvoid
, or as the operand of an explicit conversion to type cvvoid
.
(N4778, [basic.fundamental] ¶9)
Besides the itchy feeling about all those strange rules, due to the limited ways it can be used it often comes up as a painful special case when writing templates; most often it feels like we would like it to behave more like std::monostate
.
Let's imagine for a moment that instead of the quotation above, the standard said about void
something like
It's a type with definition equivalent to:
struct void { void()=default; template<typename T> explicit void(T &&) {}; // to allow cast to void };
while keeping the void *
magic - can alias any object, data pointers must survive the roundtrip through void *
.
This:
void
type "proper";return
with no expression is allowed for void
and that "flowing off" of a void
function is equivalent to return;
);Besides being compatible, this would provide:
void *
, operating as for char *
, which is a common extension, quite useful when manipulating binary buffers.Now, besides the possibly altered return values of <type_traits>
stuff, what could this possibly break in code that is well-formed according to current (C++17) rules?
In C, void main() has no defined(legit) usage, and it can sometimes throw garbage results or an error. However, main() is used to denote the main function which takes no arguments and returns an integer data type.
Now ask yourself the same questions. There is no illegal void member, you meant void* . void is an incomplete type, and the only possible members of incomplete types allowed in union and struct (as last member) are incomplete array types of complete types.
The reason not to use void main() is that the language standard doesn't permit it, any more than it permits double main(long long foo, time_t bar) .
A void pointer(void* ) is a pointer that has no associated data type with it. A void pointer can hold address of any type and can be typcasted to any type. For example: int a = 10; char b = 'x'; void *unused = &a; // void pointer holds address of int 'a' unused = &b; // void pointer holds address of char 'b'
There is a proposal for this, it is p0146: Regular Void
Presented below is a struct definition that is analogous to what is proposed for void in this paper. The actual definition is not a class type, but this serves as a fairly accurate approximation of what is proposed and how developers can think about void. What should be noticed is that this can be thought of as adding functionality to the existing void type, much like adding a special member function to any other existing type that didn't have it before, such as adding a move constructor to a previously non-copyable type. This comparison is not entirely analogous because void is currently no ordinary type, but it is a reasonable, informal description, with details covered later.
struct void { void() = default; void(const void&) = default; void& operator =(const void&) = default; template <class T> explicit constexpr void(T&&) noexcept {} }; constexpr bool operator ==(void, void) noexcept { return true; } constexpr bool operator !=(void, void) noexcept { return false; } constexpr bool operator <(void, void) noexcept { return false; } constexpr bool operator <=(void, void) noexcept { return true; } constexpr bool operator >=(void, void) noexcept { return true; } constexpr bool operator >(void, void) noexcept { return false; }
It was received well in Oulu June 2016 meeting Trip Report:
Regular void, a proposal to remove most instances of special-case treatment of void in the language, making it behave like any other type. The general idea enjoyed an increased level of support since its initial presentation two meetings ago, but some details were still contentious, most notably the ability to delete pointers of type void*. The author was encouraged to come back with a revised proposal, and perhaps an implementation to help rule out unexpected complications.
I chatted with the author and he confirmed that it is basically waiting for an implementation, once there is an implementation he plans on bringing the proposal back.
There is extensive discussion in the paper about what changes and why, it is not really quotable as a whole but the FAQ questions addressed are:
- Doesn't This Proposal Introduce More Special-Casing for void?
- Why Isn't sizeof(void) Equal to 0?
- Does This Break std::enable_if?
- In Practice, Would This Break ABI Compatibility?
- Doesn't constexpr_if Make Branching for void Easier?
- Isn't It Illogical to Support some-operation for void?
- Doesn't This Remove the Notion of "No Result?"
- Isn't This a Change to the Meaning of void?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With