Discussion:
Allow values of void
(too old to reply)
m***@gmx.de
2015-07-30 20:49:44 UTC
Permalink
Currently, declaring a variable as void is forbidden. That makes sense but
restricts some cases of generic programming.

The intent is, to allow variables of type void but make them carry no
actual data.

Consider:

template<typename R, typename Q, typename... A>
Q concat(R (*fn)(A...), Q (*fn2)(R), A&&... a)
{
R result = fn(a...);
return fn2(result);
}

int convert(float f)
{
return f;
}

void print(int a)
{
std::cout << a;
}

auto convert_and_print = std::bind(concat<int, void, float>, convert, print,
std::placeholders::_1);
convert_and_print(3.5f);

This method can be used, combined with bind to combine two methods into
one. But it fails, when the return type of the first function is void. Why
would we need that, well consider

void print(int a)
{
std::cout << a;
}
void printline()
{
std::cout << std::endl;
}
//auto print_with_ln = std::bind(concat<void, void, int>, print, printline,
std::placeholders::_1);
// Fails horribly at the moment

I consider this a bad design by the language and thus propose the following:

A variable of type void

- occupies no space (sizeof(void) == 0)
- holds no value and can not be converted to any other type.
- has an alignment of 1 (aligned to a storage unit boundary).
- defines the following operators
- defines no other operators but == and !=
- compares equal to all other void variables (up to discussion)
- is "swallowed" when given as an argument to a function. It appears as
if it wasn't given (but the expression giving this void is evaluated).


Certainly, there is a second point we have to take care of.

A function with the signature R(A..., B...) also has the signature R(A...,
void, B...) with typename R and typename...A, typename... B. This is to be
able to bind no-args functions as a function accepting a void argument, and
so forth.

With this the example print_with_ln would certainly compile and execute.

-- WE
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-07-30 21:06:29 UTC
Permalink
Post by m***@gmx.de
- is "swallowed" when given as an argument to a function. It appears as
if it wasn't given (but the expression giving this void is evaluated).
This would suddenly allow:

void f(int, int);
void g();

int main()
{
f(1, g(), 1);
}

Did f get three arguments? No, it got two, because g() was void and got
swallowed.

The above could be done today with:

f(1, (g(), 1));

This suddenly also allows dereferencing a void* pointer.

template <typename T, T (*Func)()>
void h(T *ptr)
{
*ptr = Func();
}

Should the compiler warn that ptr wasn't used?
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
m***@gmx.de
2015-07-30 21:17:42 UTC
Permalink
Post by Thiago Macieira
f(1, (g(), 1));
Note that there is a subtle difference: The way we can do it now puts a
sequence point between the evaluation of g() and 1.

This suddenly also allows dereferencing a void* pointer.
Post by Thiago Macieira
template <typename T, T (*Func)()>
void h(T *ptr)
{
*ptr = Func();
}
Should the compiler warn that ptr wasn't used?
Why is that a bad thing? I wouldn't even say that a warning was need, but
you are right that one has to define the semantics for a void pointer.
Possibly something like dereferencing a void pointer results in an
(imaginary) reference to a void value. Asignment doesn't do anything at
all, thus "it is safe" to do so.

If this was for the sake of generalizing generic programming methods, I
would happily try to come up with good stuff.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-07-30 23:49:34 UTC
Permalink
Post by Thiago Macieira
This suddenly also allows dereferencing a void* pointer.
template <typename T, T (*Func)()>
void h(T *ptr)
{
*ptr = Func();
}
Should the compiler warn that ptr wasn't used?
Why is that a bad thing? I wouldn't even say that a warning was need, but
you are right that one has to define the semantics for a void pointer.
Possibly something like dereferencing a void pointer results in an
(imaginary) reference to a void value. Asignment doesn't do anything at
all, thus "it is safe" to do so.
If this was for the sake of generalizing generic programming methods, I
would happily try to come up with good stuff.
It's like a pointer to nullptr_t: you don't read from it and you don't write
to it. It's always null.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Andrey Semashev
2015-07-30 21:23:24 UTC
Permalink
Post by m***@gmx.de
Currently, declaring a variable as void is forbidden. That makes sense but
restricts some cases of generic programming.
A variable of type void
occupies no space (sizeof(void) == 0)
No (complete) type can have sizeof(T) == 0 because this would break
strict aliasing rules (i.e. no two objects can have the same memory
address). Void is incomplete, that's why you can't create values of it
or apply sizeof to it. You're asking to make it a complete type and as
such you will have to give it a size and a non-empty set of values
(even if that set consists of a single value).

I think this is a bad idea for multiple reasons. First, I don't think
the example you've given is motivating enough to make such drastic
changes to the language type system. Second, if changed this way, void
becomes no different from other types in that you have to return its
values from void functions, and that's a breaking change. You could go
further and make void yet special in that its value is implied in void
function returns, but that doesn't strike me as a sign of good design.
Third, void values will have to be actually passed around, and that
may break ABI as well. There are probably many more reasons that I'm
forgetting now.

What instead you could propose is to relax some rules on function call
syntax so that this is allowed:

void foo();
void bar();

void concat()
{
return foo(bar());
}

This doesn't require changes to the type system and just adds some
syntax uniformity.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-07-30 22:06:07 UTC
Permalink
Post by m***@gmx.de
Currently, declaring a variable as void is forbidden. That makes sense but
restricts some cases of generic programming.
The intent is, to allow variables of type void but make them carry no
actual data.
Yes!!! Working with void types are a pain in C++ in generic code and you
are frequently forced to make needless specializations because of it. For
examples, look at things like futures, expected, etc.

Someone with Sean Parent's email on hand should CC him, since he has
expressed interested in exactly this notion and I'm sure he's given it a
fair amount of thought already.

A variable of type void
Post by m***@gmx.de
- occupies no space (sizeof(void) == 0)
This might be weird because of its implications with respect to the rest
of the language as it currently is. For instance, can you make an array of
them (you should logically be able to)? I think if we were starting from
scratch with C++, size 0 would be a reasonable choice, but IIUC, it would
be difficult to change now. That said, this doesn't rule out making
instances of void types and using them in generic code.
Post by m***@gmx.de
- defines the following operators
- defines no other operators but == and !=
- compares equal to all other void variables (up to discussion)
I see no reason to not support all of the comparisons. There's no reason
to not support <, for instance. Also, void types should be assignable. They
should be regular types.
Post by m***@gmx.de
- is "swallowed" when given as an argument to a function. It appears
as if it wasn't given (but the expression giving this void is evaluated).
I disagree here. You still should be able to pass void and have them
really be separate arguments. Consider the following example in generic
code:

//////////
template <class... Fun>
auto tuple_of_results(Fun... fun) {
return std::make_tuple(fun()...);
}
//////////

Ideally, the above code should work for any return types, including void.
If you strictly swallow void, this wouldn't work. Going further, to be
truly proper, reference-to-void also should be supported (for instance,
std::tie in generic code or std::forward_as_tuple, both of which are
common).

Here's a somewhat wacky idea, but I'm just throwing it out there. Instead
of "swallowing" void, allow passing *nothing* as an argument to a function
whose parameter is void (and consider functions that take no parameters to
be equivalent in usage, though maybe not in function type, to functions
that take a single void parameter).

For example:

//////////
template <class T>
void foo(T arg) {}

int main() {
foo<void>(); // This would work
foo<void>(void{}); // This also works (default-construct a void)
foo<void>(foo<void>()); // This also works
}
//////////

With respect to something like the definition of std::promise, it should
allow for std::promise<void> to not require a specialization and instead
just use the default template definition, and you could still use it the
same way that you would today in non-generic code with the specialization
that is specified (for instance, set_value() would just work).

In cases where "void" appears not as the sole parameter type, we can even
extrapolate and do the same thing -- keep in mind that users still need to
pass an argument, even if that argument is nothing:

//////////
// Notice that we can pass "nothing" here. This comes naturally from the
above rule.
tuple<int, void, char, void, void> foo(5, , 'a', , );
//////////

This type of syntax actually has precedent in C++ already, since this is
how you pass empty tokens to macros in C and C++.

Ultimately, if we are to have properly designed void types, they should not
be particularly special. They should just be regular types like most other
(proper) C++.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-07-30 22:40:11 UTC
Permalink
(and consider functions that take no parameters to be equivalent in usage,
though maybe not in function type, to functions that take a single void
parameter).
To avoid breaking changes, for functions declared using "void" as the
parameter type directly (I.E. where void isn't a typedef or something
dependent on a template parameter), the function type is a nullary
function, as has always been the case. On the other hand, if the parameter
type is "void" but that type came from a typedef, or is dependent on a
template parameter (these are illegal in current C++), then the function
type is unary, taking that void parameter. In this way, all existing
function declarations would have the same exact type that they always had,
but declaration of void functions in generic code would work in the way
that is expected. If you want to make a unary function that takes a void
parameter and you are in non-generic code, you can still do this by using a
typedef of void (or an identity alias, passing in void).
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-07-30 22:56:48 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
In cases where "void" appears not as the sole parameter type, we can even
extrapolate and do the same thing -- keep in mind that users still need to
//////////
// Notice that we can pass "nothing" here. This comes naturally from the
above rule.
tuple<int, void, char, void, void> foo(5, , 'a', , );
//////////
This type of syntax actually has precedent in C++ already, since this is
how you pass empty tokens to macros in C and C++.
A further thought -- if one really wanted to, this notion of a *nothing*
argument doesn't have to be specific to void types at all. You could simply
state that passing *nothing* is always shorthand for specifying default
construction of a parameter. In that case, void is nothing special at all,
but rather, it's simply a type that is default-constructible, which is why
you can pass *nothing* as an argument for a unary void function. This just
generalizes the idea of a *nothing* argument. In cases where the parameter
type needs to be deduced to T and someone passes *nothing*, then T could
just be specified to be deduced as void.

The only place this generalization of default-construct when passing
*nothing* notion falls apart, that I can think of off-hand, is with
parameters that are reference-to-void (such as set_value of a
std::promise), since a reference isn't default constructible. set_value()
worked prior to generalization because the nothing-ness was a shorthand for
a default-constructed void. It's probably possible to generalize and have
this work, but I'm not sure it's worth it. Just an interesting thought.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
m***@gmx.de
2015-07-31 00:08:16 UTC
Permalink
Post by m***@gmx.de
I see no reason to not support all of the comparisons. There's no reason
to not support <, for instance. Also, void types should be assignable. They
should be regular types.
Sounds reasonable. They should definitly be assignable, although assigning
them should be effectively a no-op.
Post by m***@gmx.de
Post by m***@gmx.de
- is "swallowed" when given as an argument to a function. It appears
as if it wasn't given (but the expression giving this void is evaluated).
I disagree here. You still should be able to pass void and have them
really be separate arguments. Consider the following example in generic
But then you'll break forward compatibility because foo() is different to
foo(void), it's just another overload. That wouldn't be favorable, because
it messes with old code.
Post by m***@gmx.de
Ultimately, if we are to have properly designed void types, they should
not be particularly special. They should just be regular types like most
other (proper) C++.

I have to disagree. To have no problems with the current implementation and
design, all operations directly regarding void have to be resolvable during
compile time. If they wouldn't, that means that they'd carry some data,
which is against the idea of this proposition.
So ultimatly, all operations with void-types, won't induce any kind of new
undefined behaviour, because either the fault can be detected at
compile-time or there is no trace of the operation left during runtime.
Post by m***@gmx.de
It's like a pointer to nullptr_t: you don't read from it and you don't
write
Post by m***@gmx.de
to it. It's always null.
Yes to the first sentence, no to the second one. Because void carries no
data, you can't read nor write anything. So one should be able to come up
with a formulation that allows dereferencing a void*, but doesn't allow
writing/reading from it (so it allows operations with a void object - no
reading or writing necessary and also compile-time static)
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-07-31 00:39:39 UTC
Permalink
Post by m***@gmx.de
I see no reason to not support all of the comparisons. There's no reason
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
to not support <, for instance. Also, void types should be assignable. They
should be regular types.
Sounds reasonable. They should definitly be assignable, although assigning
them should be effectively a no-op.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by m***@gmx.de
- is "swallowed" when given as an argument to a function. It appears
as if it wasn't given (but the expression giving this void is evaluated).
I disagree here. You still should be able to pass void and have them
really be separate arguments. Consider the following example in generic
But then you'll break forward compatibility because foo() is different to
foo(void), it's just another overload. That wouldn't be favorable, because
it messes with old code.
Not really. Regarding the differing function types, I address that in a
later reply. Regarding if two overloads exist -- one where it's a nullary
function and one where it's a unary void function, you could just state
that the nullary function is a better match if you pass *nothing* whereas a
unary function taking a void parameter is a better match if you actually
pass in an expression of type void. There might be more to it than this,
but I don't see a fundamental blocker here.
Post by m***@gmx.de
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Ultimately, if we are to have properly designed void types, they should
not be particularly special. They should just be regular types like most
other (proper) C++.
I have to disagree. To have no problems with the current implementation
and design, all operations directly regarding void have to be resolvable
during compile time. If they wouldn't, that means that they'd carry some
data, which is against the idea of this proposition.
I'm not sure what you are disagreeing with. As far as I can tell we are in
agreement. Can you clarify with an example of where you feel we disagree? A
void type wouldn't carry any data at all as it would be logically
stateless. A stateless type is still a regular type even though most of its
operations are no-ops. The only thing a void instance needs is a unique
address with respect to other voids if the address is ever observed, which
is just a (sometimes unfortunate) implication when dealing with types in
C++. All of the operations, such as copying and assigning are no-ops or
yield compile-time constants (comparisons). All operations are also
constexpr, etc. None of this goes against any of what you're proposing as
far as I can see. The way to get it easy to use in generic code is to
simply have it be a regular type, with the only differences being syntactic
sugar and/or *additional* functionality.
Post by m***@gmx.de
So ultimatly, all operations with void-types, won't induce any kind of new
undefined behaviour, because either the fault can be detected at
compile-time or there is no trace of the operation left during runtime.
Can you give an example of what you mean? How would this introduce "new
undefined behavior." There shouldn't be anything new here at all regarding
undefined behavior, since void would just be like any other C++ type. There
shouldn't be anything special about it at all. It should just be a
stateless type.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-07-31 00:54:12 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Can you give an example of what you mean? How would this introduce "new
undefined behavior." There shouldn't be anything new here at all regarding
undefined behavior, since void would just be like any other C++ type. There
shouldn't be anything special about it at all. It should just be a
stateless type.
To be clear, this is (effectively) all that the complete void type would
be, in my opinion:

//////////
struct void {};
constexpr bool operator ==(void, void) noexcept { return true; }
constexpr bool operator <=(void, void) noexcept { return true; }
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 false; }
//////////

And that is that. Everything else would just effectively syntactic sugar,
such as an empty argument meaning "default-constructed-void". There
shouldn't need to be very many changes to the language, and backwards
compatibility can be maintained in the way I described earlier.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
s***@gmail.com
2015-07-31 04:50:08 UTC
Permalink
Since my name was sucked into this - There are many ways the standard tries to avoid 0, I've considered giving a short talk titled "Nothing is Unavoidable". For every problem avoided, another is created. I'd like to see as many of these repaired as possible. I totally understand that many of these would break backward compatibility in a number of ways - but making void model Regular would make writing generic code _so_ much nicer.

empty classes and structs should have sizeof(0) to avoid inefficiencies and compressed pair hacks.
f(void) and f() are two different signatures - one takes a single argument of type void, the other takes no arguments.
void* should be a pointer to nothing, not a pointer to anything.
int a[0]; Should be perfectly legal. As should void a[42];
Aliasing rules should only apply to objects with size. Two objects of zero size can share the same address.
void x; should be legal, as should void x = f() when x returns void.

Babylonians "discovered" zero some 2400 years ago - but it took nearly 2000 years to find it's way into common use. Zero makes us uncomfortable because it is a discontinuity, but it should be dealt with directly instead of trying to hide or avoid it.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-07-31 06:04:46 UTC
Permalink
Post by s***@gmail.com
Since my name was sucked into this
Thanks for replying, Sean!
Post by s***@gmail.com
- There are many ways the standard tries to avoid 0, I've considered
giving a short talk titled "Nothing is Unavoidable". For every problem
avoided, another is created. I'd like to see as many of these repaired as
possible. I totally understand that many of these would break backward
compatibility in a number of ways - but making void model Regular would
make writing generic code _so_ much nicer.
Agreed, and please give that talk.

empty classes and structs should have sizeof(0) to avoid inefficiencies and
Post by s***@gmail.com
compressed pair hacks.
I agree, though I think this is the only thing that would be considerably
difficult to actually change in the language, as user-code can and likely
does depend on objects of the same type having different addresses,
sometimes in subtle ways that would be difficult to gleen. Even something
as common and fundamental as using a pointer as an iterator into an array
breaks down once you have empty types. There are unfortunately common
assumptions and uses of addresses in existence that would break if we had
size 0 objects. IMO, getting them to be size 0, specifically, just seems
like a much harder thing to fix, and the gains aren't that much when
compared to the benefits of simply having void be a regular type.

f(void) and f() are two different signatures - one takes a single argument
Post by s***@gmail.com
of type void, the other takes no arguments.
Ideally I want this, but it means that existing code that declares
foo(void) now changes meaning. I know you favor not worrying about breaking
code, but realistically this can have pretty far-reaching ramifications. In
practice it's ABI breaking and things like void foo(); void foo(void) {}
would no longer refer to the same functions. It shouldn't hurt generic code
to just say that foo(void) is unary if and only if "void" is actually a
typedef of void or dependent on a template parameter. As long as
invocations of a nullary function and a function taking a single void
parameter work the same way (perhaps only differing in which is a better
match when passing in nothing), I think this has the same affect without
being anymore difficult to write generic code.
Post by s***@gmail.com
void* should be a pointer to nothing, not a pointer to anything.
+1 though would this require doing anything at the language level other
than removing implicit casts to void and requiring the explicit cast to be
reinterpret_cast rather than static_cast? The language already allows
converting pointers between alignment-compatible types, so unless that
changes, too, this change seems not too tough. It's more of a change in how
people should think about void* than how it's represented at the core
language level.

int a[0]; Should be perfectly legal. As should void a[42];
+1 FWIW this at least already works for std::array and dynamically
allocated arrays.
Post by s***@gmail.com
Aliasing rules should only apply to objects with size. Two objects of zero
size can share the same address.
Again, I agree on principle, but I think the ramifications of this are
probably too much, since existing types that are empty could all of a
sudden have properties changed that could break code in really subtle ways.
The only way I see something like this actually getting into the language
is if when you declare a potentially-empty type, you'd do so in a way that
explicitly permits the type to actually be size 0 and have modern aliasing
rules. Of course, in new code you'd probably *always* want to declare every
one of your types that way, so that sucks. This just seems like the hardest
part of all of this to actually fix in the language. Everything else seems
feasible and not even very controversial.

void x; should be legal, as should void x = f() when x returns void.
+1
Post by s***@gmail.com
Babylonians "discovered" zero some 2400 years ago - but it took nearly
2000 years to find it's way into common use. Zero makes us uncomfortable
because it is a discontinuity, but it should be dealt with directly instead
of trying to hide or avoid it.
Give your talk! I feel like most people who do generic programming should
understand this, but it's not always immediately clear for some reason.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Matthew Woehlke
2015-07-31 14:04:18 UTC
Permalink
On 2015-07-30 18:06, 'Matt Calabrese' via ISO C++ Standard - Future
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Ultimately, if we are to have properly designed void types, they should not
be particularly special. They should just be regular types like most other
(proper) C++.
Food for thought:

Python has no concept of functions that do not have return values; a
function that does not explicitly return, or just says 'return', returns
None.

So this is legal:

def foo():
return

a = foo()
# 'a' has value 'None'

...but this is not:

def foo():
return

foo(None) # error, 'foo' takes 0 arguments, given 1
foo(foo()) # error, 'foo' takes 0 arguments, given 1
--
Matthew
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-07-31 18:41:34 UTC
Permalink
Post by Matthew Woehlke
Python has no concept of functions that do not have return values; a
function that does not explicitly return, or just says 'return', returns
None.
return
a = foo()
# 'a' has value 'None'
return
foo(None) # error, 'foo' takes 0 arguments, given 1
foo(foo()) # error, 'foo' takes 0 arguments, given 1
Yeah. I think if we were starting from scratch, we would want invoking a
nullary function with the result of a function that returns void to be a
compile error. Only a function that takes void as a parameter should be
able to be passed an expression that result in void (no different from
other types, void just happens to be a stateless type). The only reason we
probably should allow invoking a nullary function with an expression that
yields void is because it means code doesn't have to be rewritten in order
for people to take advantage of this (for instance, code like the
promise<void> specialization doesn't have to change in order for people to
pass in the result of a function call returning void to set_value()). Then
again, maybe that's not something to worry about and we just allow code to
break here in order to force people to update their code (which usually
means simplify their code). It also implies, imo, that the changes
regarding void should impact library and not just core (we'd be more
obligated to remove the promise<void> specialization, which may be a good
thing anyway).
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-07-31 18:44:05 UTC
Permalink
Then again, maybe that's not something to worry about and we just allow
code to break here in order to force people to update their code (which
usually means simplify their code).
I guess "break" is poor phrasing here. It's more just that they wouldn't be
able to take advantage of passing void without making changes to their
code.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Jared Grubb
2015-07-31 05:16:59 UTC
Permalink
I actually had a wish for this a while back. I had code that was doing
something like JSON visitation:

auto visit_string(VisitorType& visitor) {
return visitor(stuff...);
}

and then had another member like this:

auto visit_array(VisitorType& visitor) {
++this->indentation;
auto result = visitor(stuff...);
--this->indentation;
return result;
}

The first one works with visitors that return 'void', but the second one
does not compile -- even though it's conceptually the same thing.

I think others are making good arguments for and against but just wanted to
chime in and express my support.

Jared

P.S. I did use an RAII-style thing in the end to work around this issue,
but I would still prefer the above form.
Post by m***@gmx.de
Currently, declaring a variable as void is forbidden. That makes sense but
restricts some cases of generic programming.
The intent is, to allow variables of type void but make them carry no
actual data.
template<typename R, typename Q, typename... A>
Q concat(R (*fn)(A...), Q (*fn2)(R), A&&... a)
{
R result = fn(a...);
return fn2(result);
}
int convert(float f)
{
return f;
}
void print(int a)
{
std::cout << a;
}
auto convert_and_print = std::bind(concat<int, void, float>, convert,
print, std::placeholders::_1);
convert_and_print(3.5f);
This method can be used, combined with bind to combine two methods into
one. But it fails, when the return type of the first function is void. Why
would we need that, well consider
void print(int a)
{
std::cout << a;
}
void printline()
{
std::cout << std::endl;
}
//auto print_with_ln = std::bind(concat<void, void, int>, print,
printline, std::placeholders::_1);
// Fails horribly at the moment
A variable of type void
- occupies no space (sizeof(void) == 0)
- holds no value and can not be converted to any other type.
- has an alignment of 1 (aligned to a storage unit boundary).
- defines the following operators
- defines no other operators but == and !=
- compares equal to all other void variables (up to discussion)
- is "swallowed" when given as an argument to a function. It appears
as if it wasn't given (but the expression giving this void is evaluated).
Certainly, there is a second point we have to take care of.
A function with the signature R(A..., B...) also has the signature R(A...,
void, B...) with typename R and typename...A, typename... B. This is to be
able to bind no-args functions as a function accepting a void argument, and
so forth.
With this the example print_with_ln would certainly compile and execute.
-- WE
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Arthur O'Dwyer
2015-08-01 18:40:07 UTC
Permalink
Post by m***@gmx.de
Currently, declaring a variable as void is forbidden. That makes sense but
restricts some cases of generic programming.
The intent is, to allow variables of type void but make them carry no
actual data.
I've wanted this idea for a while, but I think you need to scale back the
scope of it to just what's *actually* needed *today* for generic
programming.
For example, there's no reason to support sizeof(void), because generic
code doesn't use that construct — or if it does, it's already doing crazy
things with memory allocation. (Also, there are 100% excellent reasons to
keep sizeof(void) a SFINAE failure. Suddenly defining it would break a lot
of existing code.)

Things I've wanted from void variables can be summed up as


T f(); T g() { return f(); } // should work even with T=void (and it
does already today!)

T f(); void g(T); ... g(f()); // should work even with T=void

struct s { T m; }; // should work even with T=void

T f(); T v = f(); // should work even with T=void

In other words, we don't need any "operations on void" other than the
ability to default-construct "void" variables; to initialize and assign to
"void" variables (from expressions whose own type is "void"); and to pass a
single void argument as the parameter to a function taking no arguments.

The ability to pass a single void argument to a function taking no
arguments is, I think, the most palatable first step.
(Or second step, if we count void f() { return g(); } as the first step,
already taken. A good proposal would do some historical research there into
why that feature exists.)

There is no need to support "collapsing" void arguments (e.g. the ability
to write f(((void)1, 2)) as f((void)1, 2)).
There is no need to support arithmetic, relational, or logical operations
on void operands.
There is no need to define the size or alignment of void objects; being
no-ops, they wouldn't participate in class layout anyway.
Post by m***@gmx.de
template<typename R, typename Q, typename... A>
Q concat(R (*fn)(A...), Q (*fn2)(R), A&&... a)
{
R result = fn(a...);
return fn2(result);
}
This method can be used, combined with bind to combine two methods into
one. But it fails, when the return type of the first function is void. Why
would we need that, well consider
A variable of type void
- occupies no space (sizeof(void) == 0)
- holds no value and can not be converted to any other type.
- has an alignment of 1 (aligned to a storage unit boundary).
- defines the following operators
- defines no other operators but == and !=
- compares equal to all other void variables (up to discussion)
- is "swallowed" when given as an argument to a function. It appears
as if it wasn't given (but the expression giving this void is evaluated).
Certainly, there is a second point we have to take care of.
A function with the signature R(A..., B...) also has the signature R(A...,
void, B...) with typename R and typename...A, typename... B. This is to be
able to bind no-args functions as a function accepting a void argument, and
so forth.
With this the example print_with_ln would certainly compile and execute.
-- WE
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-01 22:54:54 UTC
Permalink
there's no reason to support sizeof(void), because generic code doesn't
use that construct — or if it does, it's already doing crazy things with
memory allocation.
Sorry, but I have to call you out on that as it's simply not true. Even if
you ignore the array case that was already mentioned, what about
implementing something like a variant (assuming you aren't using a union
internally, just for sake of the example)? Very commonly, you just make raw
storage that has the maximum size of all of the possible types, and an
alignment that satisfies the alignment requirements of all of the possible
types. Great, now what if one of those types happens to be void? Now all of
a sudden the code that retrieves the maximum size of all of the types would
break as soon as it internally does sizeof(void). If you need more
grounding for why this type of thing comes up in practice, consider the
following (for the record, this is based on actual code that I've worked
with very frequently in the past).

//////////
auto result = visit(function, some_variant);
//////////

In case it is not clear, here you are invoking function on a variant,
dispatching accordingly based on what type is in the variant. Sometimes the
return type of the invoked function differs depending on what was stored in
the variant at run time, so the overall result can be represented as a
discriminated union (another variant) of the results. If one of the return
types happened to be void, then you have to work around it again.

We shouldn't start by saying "oh, you'd never have to do that with void in
generic code, so it can differ from 'normal' types here." That is how we
got into this mess to begin with. It's a flawed rationale right from the
start. void IS just a type, like any other. Stop special casing it.
(Also, there are 100% excellent reasons to keep sizeof(void) a SFINAE
failure. Suddenly defining it would break a lot of existing code.)
I exploit SFINAE a lot and that's a real stretch. Obviously this is
necessarily a breaking change because of how easy it is to introspect C++,
but in that sense there is pretty much nothing that's not a breaking change
in the language. Give some practical, real world examples of where
sizeof(void) being an error breaks "a lot of code" in a way that shouldn't
have been written some other way to begin with.

There is no need to support "collapsing" void arguments
+1

There is no need to support arithmetic, relational, or logical operations
on void operands.
No one is advocating arithmetic operations. We just want the type to be
Regular. Comparisons *always* make sense. Let's stop trying to make void
anything less than a regular type. Even if it's not immediately obvious to
you why you'd want to use any one of the operations expected of regular
types, you're going to simply be wrong as soon as you write any generic
code involving that operation. A simple example of generic code where you'd
want comparisons for void:

//////////
// Check that the results of function calls match
// when given different objects.
template<class T, class... Funs>
bool results_are_equal(T a, T b, Funs... funs)
{
return std::forward_as_tuple(funs(a)...)
== std::forward_as_tuple(funs(b)...);
}
//////////

If one or more of the functions happens to return void, then that's
perfectly fine. All instances of such a stateless type are equal. Just
because you know at compile time that the types are equal doesn't mean that
the operation itself is illogical.

The other comparison operators are just as sensible.
There is no need to define the size or alignment of void objects; being
no-ops, they wouldn't participate in class layout anyway.
Again, we are talking about generic code. If you make the operations an
error rather than giving a sensible result (and there are perfectly
sensible results here), then all you're doing if forcing people to work
around that in generic code.

As far as "getting things in gradually by making only some operations
valid," I don't think that is at all any easier than simply stating that
void is a complete type equivalent to:

//////////
struct void {};
constexpr bool operator ==(void, void) noexcept { return true; }
// And define the other comparisons as I did in another reply
//////////

This is very simple, and because you're not describing void as still some
special entity in the language, the rules of other types apply without
doing anything special (I.E. sizeof and alignas work fine). This also
effectively punts on the question of is sizeof(void) == 0, since it makes
no explicit mention of an exact size requirement, just like we do with most
other types in the language.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-08-01 23:59:13 UTC
Permalink
On Saturday 01 August 2015 15:54:54 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Sorry, but I have to call you out on that as it's simply not true. Even if
you ignore the array case that was already mentioned, what about
implementing something like a variant (assuming you aren't using a union
internally, just for sake of the example)? Very commonly, you just make raw
storage that has the maximum size of all of the possible types, and an
alignment that satisfies the alignment requirements of all of the possible
types. Great, now what if one of those types happens to be void? Now all of
a sudden the code that retrieves the maximum size of all of the types would
break as soon as it internally does sizeof(void). If you need more
grounding for why this type of thing comes up in practice, consider the
following (for the record, this is based on actual code that I've worked
with very frequently in the past).
The problem isn't that a variant class couldn't be made to work with void
under those circumstances.

The problem is that people are using void in SFINAE contexts (usually, sizeof)
and expecting it to fail. Suddenly making it succeed and yield zero may break
a lot of code, quite possibly silently.

Any new use we make of void should be in contexts that it couldn't have been
used before, not even in SFINAE.

So while I'd like to do:

template <typename T> void f(T *ptr, T (*fptr)())
{
*ptr = fptr();
}

work without specialisation for f<void> (real use-case, see Qt's
qobjectdefs_impl.h), we can probably not do:

template <typename T> void f(T value, void (*fptr)(T));

because some poor-man's enable_if may have been using expansion to a void
parameter.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-03 01:12:31 UTC
Permalink
Post by Thiago Macieira
The problem is that people are using void in SFINAE contexts (usually, sizeof)
and expecting it to fail. Suddenly making it succeed and yield zero may break
a lot of code, quite possibly silently.
Again, I understand that this is the claim, but I doubt that it is common
or something to be horribly concerned about. Places where sizeof is used
for SFINAE exploitation generally have better alternatives. If you have
actual real code that breaks because of this and it's common, not easily
diagnosable, and not easily fixable, post it so that we are not talking in
hypotheticals. Modern SFINAE exploitation usually does not rely on sizeof.
SFINAE exploitation of expressions often uses decltype as opposed to
sizeof, since sizeof can force premature instantiation of the definition of
the operand and also because it can cause an undesirable substitution
failure with respect to void already. If code is using expansion to a void
parameter to force substitution failure, I doubt it is horribly common in
user code (is there an easy way to figure this out?). Even if it were,
sacrificing that seems very reasonable to me, given that there are more
common alternatives regarding SFINAE exploitation that have been in use for
quite a while. Making generic code easier to write correctly and removing
the need for specializations like std::promise<void> seems much more
important to the future of C++ than not breaking weird hacks that could
have/should have be done in other ways anyway.

If we are to talk about SFINAE, there are very few things you can change in
the language at all that wouldn't break some hypothetical SFINAE
exploitation because of C++'s introspection capabilities. I don't think it
makes sense to hold back progress because of that. The nature of void as an
incomplete type isn't particularly unique in this regard.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-08-03 02:30:35 UTC
Permalink
On Sunday 02 August 2015 18:12:31 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Thiago Macieira
The problem is that people are using void in SFINAE contexts (usually, sizeof)
and expecting it to fail. Suddenly making it succeed and yield zero may break
a lot of code, quite possibly silently.
Again, I understand that this is the claim, but I doubt that it is common
or something to be horribly concerned about. Places where sizeof is used
for SFINAE exploitation generally have better alternatives.
There being better alternatives does not imply the better alternatives were
used. Changing behaviour of existing code requires checking how much code
would be affected.

We're both saying that the number is non-zero. So the burden will fall on you
to prove that this non-zero number is small enough and easily fixed.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
If you have
actual real code that breaks because of this and it's common, not easily
diagnosable, and not easily fixable, post it so that we are not talking in
hypotheticals. Modern SFINAE exploitation usually does not rely on sizeof.
SFINAE exploitation of expressions often uses decltype as opposed to
decltype is a C++11 trick. Please look at the large codebase of pre-C++11
SFINAE.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
If we are to talk about SFINAE, there are very few things you can change in
the language at all that wouldn't break some hypothetical SFINAE
exploitation because of C++'s introspection capabilities. I don't think it
makes sense to hold back progress because of that. The nature of void as an
incomplete type isn't particularly unique in this regard.
There are few things that are syntactically valid but would produce an error
during template substitution. Changing those risks breaking SFINAE.

More often, when we change the language, we make code that wasn't
syntactically valid before become valid

Anyway, I'm not disagreeing with some uses of void. I'm simply agreeing with
Arthur that we should go very carefully and enable only what's really
necessary.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-03 03:46:27 UTC
Permalink
Post by Thiago Macieira
decltype is a C++11 trick. Please look at the large codebase of pre-C++11
SFINAE.
Expression SFINAE wan't actually explicitly in the standard prior to C++11,
so sizeof on an expression yielding void back then was not portable to
begin with, even regarding substitution failure. If the concern with
respect to sizeof really is breakage of pre-C++11 code that was not
portable even then, then I don't see how that is a worthwhile concern. We
didn't have portable expression SFINAE until we also had decltype.

Anyway, by the time any of these changes would come in, C++11 will have
been at a minimum of 6 years old (or longer, depending on if something like
this would even be proposed by C++17). Are we really going to hold
ourselves back because of that?

As far as possible usage of void as a means to cause substitution to fail
by way of making a function have a void parameter, I find that more
compelling, and yes, that can potentially cause some code to change in
observable behavior. For that case, however, whether or not sizeof works
for void has absolutely no bearing. That case would necessarily break since
passing of void or reference-to-void is one of the more common things to
come up in generic code that we'd like to fix (It's the primary reason we
need a std::promise<void> specialization, for example, or see my tuple
example, or, in the more abstract, any generic code where you need to
invoke a function with an instance of a type that is dependent on a
template parameter where that type may or may not be void). So sizeof or no
sizeof, I think people would just have to accept that kind of change.

Getting back to whether sizeof would work or not -- if you don't allow
sizeof to work on void, I find it hard to believe that you'd be able to do
something as simple as create an array of them in the language without even
more special casing. Even if you did special-cased that, you'd probably
also need to special case pointer arithmetic on it. In short, if we only go
partway toward making void a proper type, we'll just be trading some
special casing for other special-casing further down the line, and that
special casing still needs to be specified in the standard and understood
by users of the language.

To restate, I do not advocate sizeof(void) == 0, I just advocate void being
a complete type in the manner expressed earlier, which implies that sizeof
and alignas work perfectly fine. sizeof *anything* being 0 in the language
really does break a lot of assumptions in the language and would have
implications on things as basic as arrays, etc. It would be great if we
eventually got to allowing types to truly be empty, but that is not at all
necessary to improve the state of generic programming in C++. The size of a
type being allowed to be 0 is orthogonal and I don't think it's worth
addressing in a proposal that simply makes void a regular type.

Anyway, I'm not disagreeing with some uses of void. I'm simply agreeing with
Post by Thiago Macieira
Arthur that we should go very carefully and enable only what's really
necessary.
My stance is that it's *all* really necessary if the goal truly is to not
have to needlessly special-case void when writing generic code. The claims
I'm refuting are that certain operations "don't make sense" for void. They
*do* in fact make sense, just like they make sense for other types. I am
also not at all convinced that bringing the proper changes in gradually
will make the transition any easier, whether for users or for specification
in the standard. The proper change (starting by making void a complete,
Regular type, and the other minimal changes described in earlier replies)
can be specified relatively easily, and because it doesn't rely on void
being something particularly special in the language, the rules for
"normal" types apply.

If people are genuinely concerned about breaking of some oddball SFINAE
hacks (and I say this as someone who is very liberal with usage of SFINAE
hacks), spreading out the changes only succeeds at delaying that breakage
and delays us getting a proper solution. Ultimately, these changes should
eventually be made if we want to end the special-casing of void in generic
code, and so ultimately users would have to update such code regardless of
whether the changes were made in C++17, or the next standard, or in some
future standard after that.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Arthur O'Dwyer
2015-08-03 04:41:55 UTC
Permalink
On Sun, Aug 2, 2015 at 8:46 PM, 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
As far as possible usage of void as a means to cause substitution to fail
by way of making a function have a void parameter, I find that more
compelling, and yes, that can potentially cause some code to change in
observable behavior. For that case, however, whether or not sizeof works
for void has absolutely no bearing. That case would necessarily break since
passing of void or reference-to-void is one of the more common things to
come up in generic code that we'd like to fix (It's the primary reason we
need a std::promise<void> specialization, for example, or see my tuple
example, or, in the more abstract, any generic code where you need to
invoke a function with an instance of a type that is dependent on a
template parameter where that type may or may not be void).
I think it's extremely debatable whether
std::tuple<int, void, int>
or
std::array<void, 3>
*ought* to be permitted by the language.

I'm personally interested in getting promise<void> and future<void> to work
without special cases, but for those I don't think you need to evaluate
sizeof(void) or do pointer arithmetic on void pointers or make arrays of
void... and you *definitely* don't need to evaluate void operands to
operator==! That's why I'm pushing back on this idea a bit: Let's see a
concrete situation where the current state of void is causing problems, and
see what the minimal fix would be for that situation.

For example, what if the following class template were proposed for
standardization?

template<class T> struct regularize {
T t;
T& get() { return t; } // assume we also provide const, rvalue, etc.
forms
void set(T s) { t = s; }
};
template<class T> struct regularize<T&> {
T *t;
T& get() const { return *t; }
void set(T& s) { t = &s; }
};
template<> struct regularize<void> {
void get() const {}
void set() {}
};

Would proper use of regular<T> in place of raw T solve *all* the current
use-cases?
Well, no, it wouldn't; but at least now we can focus on a smaller number of
remaining problem cases.
I nominate

template<class T> struct simple_future {
regularize<T> rt;
void set(T p) { rt.set(p); }
};

Here we have a parameter of void type, which is currently disallowed.[*]
Once we allow that, we also have to allow the name of that parameter to
denote an expression of void type.
And then we also need to allow calling a nullary function with a single
parameter of void type.
...Or, something else needs to happen, which I merely haven't thought of
yet.

–Arthur

[*] ...but only if the void type is template-dependent. If the void comes
from a non-dependent typedef, it's accepted.
This might be intended to deal with the problem case of

template<class T> void g(T) {}
int main() {
g(); // Should this call g<void>(void)?
g(g(1)); // Should this?
}

That is, even if we allow void-type arguments to functions, we might need
to specify that void *still* can't be *deduced* as a parameter type; void
arguments must appear only in non-deduced contexts.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-03 06:02:44 UTC
Permalink
Post by Arthur O'Dwyer
On Sun, Aug 2, 2015 at 8:46 PM, 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
As far as possible usage of void as a means to cause substitution to fail
by way of making a function have a void parameter, I find that more
compelling, and yes, that can potentially cause some code to change in
observable behavior. For that case, however, whether or not sizeof works
for void has absolutely no bearing. That case would necessarily break since
passing of void or reference-to-void is one of the more common things to
come up in generic code that we'd like to fix (It's the primary reason we
need a std::promise<void> specialization, for example, or see my tuple
example, or, in the more abstract, any generic code where you need to
invoke a function with an instance of a type that is dependent on a
template parameter where that type may or may not be void).
I think it's extremely debatable whether
std::tuple<int, void, int>
or
std::array<void, 3>
*ought* to be permitted by the language.
Why? Simply stating that they shouldn't be permitted without any rationale
isn't convincing. void can simply be thought of as a stateless type.
Treating it as something *less* than that is precisely the problem we have
today. Sean's analogy with respect to the notion of 0 is really on point.
Don't act like 0 is not a number. Just because void has no state doesn't
mean that it's not a type. It's just a type with no state. People make
empty types in C++ all of the time, and there is no reason to think of void
as anything different. Pretending that void is special is the source of why
generic code in C++ requires special casing when dealing with void. We
should be fixing the problem rather than trying to put band-aids over some
of the issues.

The way you make void easy to use in generic code is you simply *make it a
regular type with no state*. 0 is a number.

I'm personally interested in getting promise<void> and future<void> to work
Post by Arthur O'Dwyer
without special cases, but for those I don't think you need to evaluate
sizeof(void) or do pointer arithmetic on void pointers or make arrays of
void... and you *definitely* don't need to evaluate void operands to
operator==!
... How would making operator== defined for void be a problem for you or
for anyone? Why would you draw the line there even if you didn't understand
my examples of where it comes up in generic code? I am telling you flat
out, with examples, where equality can come up in generic code (really
*all* object
types should be regular, but we can leave that for different thread...).
There is nothing special about an instantiable void that means that ==
should not work. Just because you know at compile time that two instances
of a type are equal does not mean that it is illogical to compare them.
It's perfectly fine and well defined and can come up in generic code. Why
do we define equality for array<int, 0>? Do you feel that this should be a
compile error, instead? It's just an empty type, after all. The reason it's
important is because the size of the array can be dependent and yet you
still may need to logically compare two instances. Your code shouldn't
break simply because you reached the 0 case. 0 is just a number. The same
is true for void -- the result of a function may be a dependent type
(including void) but you still may logically compare the result in your
generic code. It's just a type without state. 0 is just a number.

For example, what if the following class template were proposed for
Post by Arthur O'Dwyer
standardization?
template<class T> struct regularize {
T t;
T& get() { return t; } // assume we also provide const, rvalue, etc.
forms
void set(T s) { t = s; }
};
template<class T> struct regularize<T&> {
T *t;
T& get() const { return *t; }
void set(T& s) { t = &s; }
};
template<> struct regularize<void> {
void get() const {}
void set() {}
};
Would proper use of regular<T> in place of raw T solve *all* the current
use-cases?
No. I already gave a simple example of what that wouldn't cover -- a
function that returns a tuple of results of function calls. I'm sorry that
you do not accept that specifically as important, but it can and has come
up in generic code, including actual code that I have written and that is
used. If you haven't encountered situations such as these then you're just
not writing generic code that deals with void when it is dependent and used
with composition of other algorithms. Creating helper templates that try to
push some of the special casing off does not accomplish the goal of
actually removing the need for hacks and special casing, it just tries to
standardize even more hacks. If we actually fix the language, we don't need
these workarounds at all. 0 is just a number.
Post by Arthur O'Dwyer
Well, no, it wouldn't; but at least now we can focus on a smaller number
of remaining problem cases.
I nominate
template<class T> struct simple_future {
regularize<T> rt;
void set(T p) { rt.set(p); }
};
Don't you see what you are doing? You're just introducing more and more
facilities to handle special casing rather than simply removing the need
for the special casing. You are advocating that whenever people write
generic code where void may come up, they need to go through a level of
indirection though some standard template with specializations and
overloads. Stop. This is the kind of stuff that we are already doing. We
want to stop the need for specializations and extra indirection, and
knowledge of tricks in order to write very simple generic code. We don't
want to standardize more hacks. Fix the language. 0 is a number. void is a
stateless type.

...Or, something else needs to happen, which I merely haven't thought of
Post by Arthur O'Dwyer
yet.
... Or you could make void a regular type (the definition of which is
already smaller than your incomplete attempts at workarounds). This
*actually* covers the cases, including the ones that we didn't enumerate in
this thread, without adding any templates to the standard library that do
not solve the underlying problem while still leaving generic code that
deals with void as needlessly complicated to write. If we just make void
regular, users can write generic code that [potentially] deals with void
exactly as they'd write it now if they weren't covering the void "corner
case." Stop treating void as something special. It's not.

And 0 is a number.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Paul Fultz II
2015-08-03 09:58:38 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Arthur O'Dwyer
On Sun, Aug 2, 2015 at 8:46 PM, 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
As far as possible usage of void as a means to cause substitution to
fail by way of making a function have a void parameter, I find that more
compelling, and yes, that can potentially cause some code to change in
observable behavior. For that case, however, whether or not sizeof works
for void has absolutely no bearing. That case would necessarily break since
passing of void or reference-to-void is one of the more common things to
come up in generic code that we'd like to fix (It's the primary reason we
need a std::promise<void> specialization, for example, or see my tuple
example, or, in the more abstract, any generic code where you need to
invoke a function with an instance of a type that is dependent on a
template parameter where that type may or may not be void).
I think it's extremely debatable whether
std::tuple<int, void, int>
or
std::array<void, 3>
*ought* to be permitted by the language.
They should be permitted and they should be equivalent of `void`.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Why? Simply stating that they shouldn't be permitted without any rationale
isn't convincing. void can simply be thought of as a stateless type.
Treating it as something *less* than that is precisely the problem we
have today. Sean's analogy with respect to the notion of 0 is really on
point. Don't act like 0 is not a number. Just because void has no state
doesn't mean that it's not a type. It's just a type with no state. People
make empty types in C++ all of the time, and there is no reason to think of
void as anything different. Pretending that void is special is the source
of why generic code in C++ requires special casing when dealing with void.
We should be fixing the problem rather than trying to put band-aids over
some of the issues.
Ok, an empty type or a stateless type is a type that has a state of at
least one, because the very existence of the type creates state. This is
why creating a struct of two empty types has the same state as an empty
type(ie `1 * 1 = 1`) or creating a variant of two empty types has the same
state as a boolean(ie `1 + 1 = 2`). However, `void` is different, and so
should be treated as a type with zero state. As such, using `void` as a
member to a struct(or `tuple<int, void>`) should be equivalent to `void`
because `x * 0 = 0`.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Magnus Fromreide
2015-08-03 11:35:50 UTC
Permalink
Post by Paul Fultz II
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Arthur O'Dwyer
On Sun, Aug 2, 2015 at 8:46 PM, 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
As far as possible usage of void as a means to cause substitution to
fail by way of making a function have a void parameter, I find that more
compelling, and yes, that can potentially cause some code to change in
observable behavior. For that case, however, whether or not sizeof works
for void has absolutely no bearing. That case would necessarily break since
passing of void or reference-to-void is one of the more common things to
come up in generic code that we'd like to fix (It's the primary reason we
need a std::promise<void> specialization, for example, or see my tuple
example, or, in the more abstract, any generic code where you need to
invoke a function with an instance of a type that is dependent on a
template parameter where that type may or may not be void).
I think it's extremely debatable whether
std::tuple<int, void, int>
or
std::array<void, 3>
*ought* to be permitted by the language.
They should be permitted and they should be equivalent of `void`.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Why? Simply stating that they shouldn't be permitted without any rationale
isn't convincing. void can simply be thought of as a stateless type.
Treating it as something *less* than that is precisely the problem we
have today. Sean's analogy with respect to the notion of 0 is really on
point. Don't act like 0 is not a number. Just because void has no state
doesn't mean that it's not a type. It's just a type with no state. People
make empty types in C++ all of the time, and there is no reason to think of
void as anything different. Pretending that void is special is the source
of why generic code in C++ requires special casing when dealing with void.
We should be fixing the problem rather than trying to put band-aids over
some of the issues.
Ok, an empty type or a stateless type is a type that has a state of at
least one, because the very existence of the type creates state. This is
why creating a struct of two empty types has the same state as an empty
type(ie `1 * 1 = 1`) or creating a variant of two empty types has the same
state as a boolean(ie `1 + 1 = 2`). However, `void` is different, and so
should be treated as a type with zero state. As such, using `void` as a
member to a struct(or `tuple<int, void>`) should be equivalent to `void`
because `x * 0 = 0`.
How should incomplete types other than void, like for example

struct incomplete;

be handled under your proposal, should they get the same new features that you
are proposing for void, or should void be an odd exception?

/MF
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Paul Fultz II
2015-08-03 14:39:33 UTC
Permalink
Post by Magnus Fromreide
How should incomplete types other than void, like for example
struct incomplete;
be handled under your proposal, should they get the same new features that you
are proposing for void, or should void be an odd exception?
Probably not. A union with an incomplete type member should be an
incomplete type, because it is an yet-to-be-determined size, whereas if the
union has a void member it should essentially be ignored.
Post by Magnus Fromreide
/MF
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-03 17:45:01 UTC
Permalink
Post by Paul Fultz II
Probably not. A union with an incomplete type member should be an
incomplete type
We have no reason to change the behavior of unions containing incomplete
types.
Post by Paul Fultz II
, because it is an yet-to-be-determined size, whereas if the union has a
void member it should essentially be ignored.
A union with a void member shouldn't be ignored. It's a union with a void
member. It is exactly like putting a user-defined type with no members into
a union. There is nothing special here. There is nothing special about any
of these cases. It's just, very simply, an empty type. It is not special.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-03 17:42:26 UTC
Permalink
Post by Magnus Fromreide
How should incomplete types other than void, like for example
struct incomplete;
be handled under your proposal, should they get the same new features that you
are proposing for void, or should void be an odd exception?
These changes come up because we'd be making void a complete type. It
doesn't affect other incomplete types. User-defined types that are
incomplete would remain incomplete.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Magnus Fromreide
2015-08-03 18:30:59 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Magnus Fromreide
How should incomplete types other than void, like for example
struct incomplete;
be handled under your proposal, should they get the same new features that you
are proposing for void, or should void be an odd exception?
These changes come up because we'd be making void a complete type. It
doesn't affect other incomplete types. User-defined types that are
incomplete would remain incomplete.
Would it make sense to only treat void as complete if the definition was
included, like other incomplete types?

void foo; // Error - void is incomplete

#include <void> // Contains compiler magic that "declares" the void type

void bar; // Ok (maybe, depending on the proposal)

The point of this is to allow old programs to continue working as they always
have.

/MF
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-03 18:55:11 UTC
Permalink
On Mon, Aug 03, 2015 at 10:42:26AM -0700, 'Matt Calabrese' via ISO C++
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Magnus Fromreide
How should incomplete types other than void, like for example
struct incomplete;
be handled under your proposal, should they get the same new features
that
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Magnus Fromreide
you
are proposing for void, or should void be an odd exception?
These changes come up because we'd be making void a complete type. It
doesn't affect other incomplete types. User-defined types that are
incomplete would remain incomplete.
Would it make sense to only treat void as complete if the definition was
included, like other incomplete types?
void foo; // Error - void is incomplete
#include <void> // Contains compiler magic that "declares" the void type
void bar; // Ok (maybe, depending on the proposal)
The point of this is to allow old programs to continue working as they always
have.
Maybe, but this adds complexity to usage and also can cause subtle bugs.
Worse is that it doesn't really solve the problem in the case where header
files need to include <void>, which they definitely would since template
definitions are what benefit from void being complete. An example of why
this is a problem is, what happens if one header "needs" void to be
incomplete (an old header that does something really weird with SFINAE),
but a different header defines a template and expects void to be complete
so that it can be properly used in generic code? The header with the
template definition would include <void>, and if it just so happens to be
included before the header that expects void to be incomplete, then the
old, odd-ball SFINAE code could break in a subtle way! It will either stop
compiling (good) or compile differently (bad, as you now have ODR
violations in addition to incorrect run time code). A similar kind of
breakage, but perhaps less of a problem, is if the person writing the
template definition simply forgets to include <void>. There, the template
definition then would usually break if someone uses it with void, however,
what if, because of inclusion order, some other header file brought in
<void>? Now that definition would work in that specific translation unit!
Finally, what about the case where a template that wants complete void
needs to invoke a template that expects incomplete void? Here the user is
really out of luck. The same goes for interleaving of include files that
expect void to be complete or void to be incomplete. It would be very
difficult, and some times impossible, to get things to work in this
environment. Instead, I think it makes much more sense to just allow some
breakage here.

I want to restate, though, that I think people are really overestimating
just what would break by making void complete. The standard SFINAE exploits
do not at all rely on void being an incomplete type. We are being generous
here and talking about hypothetical SFINAE exploits that have more accepted
alternatives. Unfortunately, it is considerably difficult to put a number
on if or how much actual real-world code would potentially break, because
the things that would break are very oddball, one-off SFINAE tricks that
you can't really search for. I think the only way to really get an idea of
what could break is to implement complete, regular void in a compiler, and
then go out and compile large projects to see what breaks.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-03 17:40:02 UTC
Permalink
Post by Paul Fultz II
Post by Arthur O'Dwyer
On Sun, Aug 2, 2015 at 8:46 PM, 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
As far as possible usage of void as a means to cause substitution to
fail by way of making a function have a void parameter, I find that more
compelling, and yes, that can potentially cause some code to change in
observable behavior. For that case, however, whether or not sizeof works
for void has absolutely no bearing. That case would necessarily break since
passing of void or reference-to-void is one of the more common things to
come up in generic code that we'd like to fix (It's the primary reason we
need a std::promise<void> specialization, for example, or see my tuple
example, or, in the more abstract, any generic code where you need to
invoke a function with an instance of a type that is dependent on a
template parameter where that type may or may not be void).
I think it's extremely debatable whether
std::tuple<int, void, int>
or
std::array<void, 3>
*ought* to be permitted by the language.
They should be permitted and they should be equivalent of `void`.
Well, not exactly. They should definitely be permitted, but std::tuple<int,
void, int> especially is not equivalent to void (the array one can be
"equivalent" depending on our equivalence function, but I guess that's true
for anything, though it certainly differs on type!). A set that contains
one or more null sets is not, itself, a null set. I'm not approximating
when I say that void should just be like a user-defined type that has no
members and is regular. That really is all that it should be. You should
expect tuple<int, void, int> to behave just like if you had tuple<int,
some_user_defined_empty_type, int>. That's all it is, and that is what you
expect it to be when you are writing generic code. You shouldn't try to
reintroduce special casing. It's just a type that doesn't have members.

Ok, an empty type or a stateless type is a type that has a state of at
Post by Paul Fultz II
least one, because the very existence of the type creates state. This is
why creating a struct of two empty types has the same state as an empty
type(ie `1 * 1 = 1`) or creating a variant of two empty types has the same
state as a boolean(ie `1 + 1 = 2`). However, `void` is different, and so
should be treated as a type with zero state. As such, using `void` as a
member to a struct(or `tuple<int, void>`) should be equivalent to `void`
because `x * 0 = 0`.
What? No. void is not special here. It's just like any user-defined type
without members except it happens to be a built-in type. If you put an
instance of your own empty type in a struct, that struct doesn't become
empty. If you don't see why this hurts things in practice, again, just
consider something as simple as a function that returns the result of N
function calls as a tuple of size N. There is nothing logically wrong with
this even if one of the functions doesn't have to return a type that
contains state. You should be able to access any of results, iterate over
the results, etc. Algorithms on regular types should simply work on it
because there is absolutely no reason, other than manufactured reasons, for
why they wouldn't. void is not special here. It's just an empty type.
Representing it as something less than or different than object types only
hurts things.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Paul Fultz II
2015-08-03 20:38:43 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Paul Fultz II
Post by Arthur O'Dwyer
On Sun, Aug 2, 2015 at 8:46 PM, 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
As far as possible usage of void as a means to cause substitution to
fail by way of making a function have a void parameter, I find that more
compelling, and yes, that can potentially cause some code to change in
observable behavior. For that case, however, whether or not sizeof works
for void has absolutely no bearing. That case would necessarily break since
passing of void or reference-to-void is one of the more common things to
come up in generic code that we'd like to fix (It's the primary reason we
need a std::promise<void> specialization, for example, or see my tuple
example, or, in the more abstract, any generic code where you need to
invoke a function with an instance of a type that is dependent on a
template parameter where that type may or may not be void).
I think it's extremely debatable whether
std::tuple<int, void, int>
or
std::array<void, 3>
*ought* to be permitted by the language.
They should be permitted and they should be equivalent of `void`.
Well, not exactly. They should definitely be permitted, but
std::tuple<int, void, int> especially is not equivalent to void (the array
one can be "equivalent" depending on our equivalence function, but I guess
that's true for anything, though it certainly differs on type!). A set that
contains one or more null sets is not, itself, a null set.
We aren't talking about sets. We are talking about states in a program(or
typesystem rather). A `struct` or `tuple` is the product of the states of
its members. If you define a struct with a member that has zero state, the
resulting struct will have zero state as well.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
I'm not approximating when I say that void should just be like a
user-defined type that has no members and is regular. That really is all
that it should be. You should expect tuple<int, void, int> to behave just
like if you had tuple<int, some_user_defined_empty_type, int>. That's all
it is, and that is what you expect it to be when you are writing generic
code. You shouldn't try to reintroduce special casing. It's just a type
that doesn't have members.
I thought the point was to embrace zero. Instead, you want to define `void`
with one state rather than zero state. I don't think that is very sound at
all.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Ok, an empty type or a stateless type is a type that has a state of at
Post by Paul Fultz II
least one, because the very existence of the type creates state. This is
why creating a struct of two empty types has the same state as an empty
type(ie `1 * 1 = 1`) or creating a variant of two empty types has the same
state as a boolean(ie `1 + 1 = 2`). However, `void` is different, and so
should be treated as a type with zero state. As such, using `void` as a
member to a struct(or `tuple<int, void>`) should be equivalent to `void`
because `x * 0 = 0`.
What? No. void is not special here. It's just like any user-defined type
without members except it happens to be a built-in type. If you put an
instance of your own empty type in a struct, that struct doesn't become
empty. If you don't see why this hurts things in practice, again, just
consider something as simple as a function that returns the result of N
function calls as a tuple of size N.
If one of the functions return `void` then the function should return
`void`.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
There is nothing logically wrong with this even if one of the functions
doesn't have to return a type that contains state. You should be able to
access any of results, iterate over the results, etc. Algorithms on regular
types should simply work on it because there is absolutely no reason, other
than manufactured reasons, for why they wouldn't. void is not special here.
It's just an empty type. Representing it as something less than or
different than object types only hurts things.
But by doing that, you are now injecting extra state into the program.
Perhaps, that is acceptable for your use case, in which it would be better
for the user to explicity transform`void` to an empty type, rather than
silently inject extra state.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Zach Laine
2015-08-03 20:47:50 UTC
Permalink
On Mon, Aug 3, 2015 at 3:38 PM, Paul Fultz II <***@gmail.com> wrote:

[snip]
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
What? No. void is not special here. It's just like any user-defined type
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
without members except it happens to be a built-in type. If you put an
instance of your own empty type in a struct, that struct doesn't become
empty. If you don't see why this hurts things in practice, again, just
consider something as simple as a function that returns the result of N
function calls as a tuple of size N.
If one of the functions return `void` then the function should return
`void`.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
There is nothing logically wrong with this even if one of the functions
doesn't have to return a type that contains state. You should be able to
access any of results, iterate over the results, etc. Algorithms on regular
types should simply work on it because there is absolutely no reason, other
than manufactured reasons, for why they wouldn't. void is not special here.
It's just an empty type. Representing it as something less than or
different than object types only hurts things.
But by doing that, you are now injecting extra state into the program.
Perhaps, that is acceptable for your use case, in which it would be better
for the user to explicity transform`void` to an empty type, rather than
silently inject extra state.
What extra state is injected? If I return a byte-sized void with an
undefined value that should be ignored, but you choose not to ignore it,
what can you do with it? You can't read its value -- it's empty. What is
the outward change to your program that I will observe?

Zach
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Paul Fultz II
2015-08-03 22:57:33 UTC
Permalink
Post by Zach Laine
[snip]
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
What? No. void is not special here. It's just like any user-defined type
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
without members except it happens to be a built-in type. If you put an
instance of your own empty type in a struct, that struct doesn't become
empty. If you don't see why this hurts things in practice, again, just
consider something as simple as a function that returns the result of N
function calls as a tuple of size N.
If one of the functions return `void` then the function should return
`void`.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
There is nothing logically wrong with this even if one of the functions
doesn't have to return a type that contains state. You should be able to
access any of results, iterate over the results, etc. Algorithms on regular
types should simply work on it because there is absolutely no reason, other
than manufactured reasons, for why they wouldn't. void is not special here.
It's just an empty type. Representing it as something less than or
different than object types only hurts things.
But by doing that, you are now injecting extra state into the program.
Perhaps, that is acceptable for your use case, in which it would be better
for the user to explicity transform`void` to an empty type, rather than
silently inject extra state.
What extra state is injected? If I return a byte-sized void with an
undefined value that should be ignored, but you choose not to ignore it,
what can you do with it? You can't read its value -- it's empty. What is
the outward change to your program that I will observe?
Well, for the case of a tuple it doesn't inject extra state(because `x * 1
= 1`). However, it can be observed for sum types. A type of boolean has two
states. You can easily sum two empty states together(ie variant<empty1,
empty2>) to achieve two states like a boolean, but you shouldn't be able to
achieve this with `variant<emtpy, void>`. Here's why ,say for example you
had a variant that represent the call back of two functions:

struct empty
{};

void f();
empty g();

variant<void, empty> foo(bool pick_f)
{
if (pick_f) return f();
else return g();
}

So when I call `foo` it will return the result of either `f()` or `g()`.
However, the result of `f()` has no state, so therfore the visit to the
result of foo should have just one overload:

variant<void, empty> v = foo(i);
v.visit([](empty) { std::cout << "I'm always 'empty'"; });
Post by Zach Laine
Zach
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Paul Fultz II
2015-08-03 23:06:36 UTC
Permalink
Post by Paul Fultz II
Well, for the case of a tuple it doesn't inject extra state(because `x * 1
= 1`).
I meant to write `x * 1 = x`, of course.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-03 23:36:24 UTC
Permalink
Post by Paul Fultz II
Well, for the case of a tuple it doesn't inject extra state(because `x * 1
= 1`). However, it can be observed for sum types. A type of boolean has two
states. You can easily sum two empty states together(ie variant<empty1,
empty2>) to achieve two states like a boolean, but you shouldn't be able to
achieve this with `variant<emtpy, void>`. Here's why ,say for example you
struct empty
{};
void f();
empty g();
variant<void, empty> foo(bool pick_f)
{
if (pick_f) return f();
else return g();
}
So when I call `foo` it will return the result of either `f()` or `g()`.
However, the result of `f()` has no state, so therfore the visit to the
No it shouldn't, the visitor should handle the void state. There is no
reason to treat void as special here and not doing so prevents you from
using such constructs in real generic code (not to mention that it means
variant has an arbitrary special case). void is just a type like your
struct empty{}. It just happens to be a built-in type rather than a
user-defined type. You are gaining nothing by trying to make it something
else and you are only making it harder to properly use in generic code.

variant<void, empty> v = foo(i);
Post by Paul Fultz II
v.visit([](empty) { std::cout << "I'm always 'empty'"; });
Why do you make such a claim? You've explicitly stated that you can contain
void here in the parameter list of variant, and so your visitor should
handle it in the visitation. It's nothing special. If you didn't want to
handle it, you simply wouldn't put it in the variant. Just to keep things
grounded with more practical examples, let's say that your function "foo"
is generic code living on a server and is reacting to a remote invocation
request. It invokes "f" or it invokes "g" based on the information in the
request (i.e. the function name and serialized arguments) and it locally
returns the result so that the calling-code can send a message back to the
client. All that the caller of "foo" does with the result is visit it with
a generic lambda that serializes the result that is contained and sends it
back to the client. If one of the functions happened to have a return type
of void, then so what? The serialization part of your visitation might be a
null op, or it might contain type info, or anything at all, but it still
sends back a message so that the client knows the call went through.
Everything just works because void is nothing special here, and there is
nothing logically incorrect with doing this in the visitor. You're just
sending back a reply with the result -- it just so happens that the type
may be one with no members. If you arbitrarily special-case void for
variant in such a way that it corresponds to a lack of a member, rather
than an empty member, then your visitation doesn't handle the void case and
your client never receives the reply. This isn't some strange, contrived
case and you will find that this kind of thing comes up a lot when you are
dealing with function results in generic code. This very scenario is
strikingly similar to code I've written and is in use, and is not
particularly unique. Right now, there is extra indirection to handle the
void case, but it simply should not be necessary when you think about the
problem in the abstract. The same is true for all generic code where you
are dealing with a dependent type that may or may not be void and you need
to pass along whatever state is there (which may be state that contains
nothing, such as void).

If you don't buy into such explanations and real-world examples, then
exactly what code do you have in mind that benefits from the behavior that
you suggest? I understand that you're defining what you feel it should do,
which seems to be different from any other type in the language, and yet I
see no practical rationale or real-world code to show why this would ever
be desired. As far as I can see, it just prevents people from easily
writing generic code and keeps people having to make strange workarounds
for void.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-03 20:51:36 UTC
Permalink
Post by Paul Fultz II
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Paul Fultz II
Post by Arthur O'Dwyer
On Sun, Aug 2, 2015 at 8:46 PM, 'Matt Calabrese' via ISO C++ Standard
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
As far as possible usage of void as a means to cause substitution to
fail by way of making a function have a void parameter, I find that more
compelling, and yes, that can potentially cause some code to change in
observable behavior. For that case, however, whether or not sizeof works
for void has absolutely no bearing. That case would necessarily break since
passing of void or reference-to-void is one of the more common things to
come up in generic code that we'd like to fix (It's the primary reason we
need a std::promise<void> specialization, for example, or see my tuple
example, or, in the more abstract, any generic code where you need to
invoke a function with an instance of a type that is dependent on a
template parameter where that type may or may not be void).
I think it's extremely debatable whether
std::tuple<int, void, int>
or
std::array<void, 3>
*ought* to be permitted by the language.
They should be permitted and they should be equivalent of `void`.
Well, not exactly. They should definitely be permitted, but
std::tuple<int, void, int> especially is not equivalent to void (the array
one can be "equivalent" depending on our equivalence function, but I guess
that's true for anything, though it certainly differs on type!). A set that
contains one or more null sets is not, itself, a null set.
We aren't talking about sets.
It's a metaphor. We are talking about something, in this case a tuple, that
contains members. A tuple that contains an int and an instance of a type
that just so happens to have not datamembers, does not all of a sudden
imply that the tuple is somehow void. That makes no sense. The tuple has
two members. An int and a type that has no members. That is all. There is
nothing special here.

I thought the point was to embrace zero. Instead, you want to define `void`
Post by Paul Fultz II
with one state rather than zero state. I don't think that is very sound at
all.
What are you talking about? If you want to understand why things are the
way the are, you should think about it in comparison to types that have
data. In other words, A type with 2 members, vs. a type with 1 member, vs.
a type with 0 members (any user-defined type with no members, or in this
case, void).
Post by Paul Fultz II
If one of the functions return `void` then the function should return
`void`.
That doesn't make any sense and isn't consistent with the rest of the
language. You want a tuple of the results. The first function returns an
int, the second returns void, the third returns int. The result is a
tuple<int, void, int>. Your logic for why the result should be "void" and
not tuple<int, void, int>, where you can access each individual component,
is completely flawed. All void is is an empty type.

But by doing that, you are now injecting extra state into the program.
Post by Paul Fultz II
Perhaps, that is acceptable for your use case, in which it would be better
for the user to explicity transform`void` to an empty type, rather than
silently inject extra state.
What?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Edward Catmur
2015-08-03 23:17:04 UTC
Permalink
I think the confusion here is over whether void should be more like the zero type or the unit type. Sean's mention of zero could be construed as a vote in favor of the former, but I think the rest of what he wrote indicates that he's in fact arguing for void as the unit type. Paul appears to be in favor of void as the zero type.

For what it's worth, I think we have enough unit types in C++ already, and don't see why we need one more. At the same time, void is clearly not the zero type - a function returning zero is a function that does not return.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-03 23:41:55 UTC
Permalink
Post by Edward Catmur
For what it's worth, I think we have enough unit types in C++ already, and
don't see why we need one more.
For the reasons expressed. This is genuinely important for generic code.
Dealing with void properly in generic code currently requires
specialization. This is true even in the standard library. Making void a
complete, regular type removes the need for special-casing and makes it
easier to write generic code.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Arthur O'Dwyer
2015-08-04 02:55:10 UTC
Permalink
On Mon, Aug 3, 2015 at 4:41 PM, 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Edward Catmur
For what it's worth, I think we have enough unit types in C++ already,
and don't see why we need one more.
For the reasons expressed. This is genuinely important for generic code.
Dealing with void properly in generic code currently requires
specialization. This is true even in the standard library. Making void a
complete, regular type removes the need for special-casing and makes it
easier to write generic code.
For the record, I do completely sympathize with Matt's point of view here.
C++'s special-case treatment of void *does* seem to me like a huge problem.
I just prefer to solve the problem in as conservative and incremental a
fashion as possible.

Our intuitions about what std::tuple<int,void> would mean, if it were
legal, are also 100% in agreement. That is, I disagree that it necessarily
*should* be legal, but if it *were* legal, I believe it would *have* to
have Matt's desired semantics.

Matt, what would you do about template type deduction in your system?

template<typename T> void g(T) { }
int main() {
g(); // valid or invalid?
g((void)0); // valid or invalid?
}

–Arthur
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 03:24:19 UTC
Permalink
Post by Arthur O'Dwyer
Matt, what would you do about template type deduction in your system?
template<typename T> void g(T) { }
int main() {
g(); // valid or invalid?
g((void)0); // valid or invalid?
}
It depends. The most conservative approach is to make g() invalid, and
g((void)0) valid, deducing T as void. The downside of this is that it means
that calling set_value() on something like a promise<void> would require an
explicit expression. What probably makes the most sense is to instead have
an empty argument be allowed as syntactic sugar for default-constructed
void. This allows easy use of unary void functions from both generic and
non-generic code. In other words, you'd be able to invoke
std::promise<void>'s set_value (which would now actually take const void&
or void&&), by just calling set_value() without passing in an explicit
argument. The subtlety here is if there is an overload that actually is
nullary. In such a case, the unary-void overload should be considered and
substitution should occur, but the nullary function would be considered a
better match during overload resolution:

//////////
using Void = void;

void foo() {} // foo0
void foo(Void) {} // foo1

void bar() {} // bar0
template<class T>
void bar(T) {} // bar1

void baz(Void) {}

int main()
{
foo(); // calls foo0
foo(void()); // calls foo1

bar(); // calls bar0
bar(void()); // calls bar1, deducing T as void

baz(); // calls baz
}
//////////

This is less conservative and a little more "risky," but would allow us to
remove the std::promise<void> specialization without breaking most
user-code (unless, or course, the user code was relying on the signature of
set_value, such as taking its address). Compiler-folks are probably best at
understanding the implications of allowing empty meaning
default-constructed-void obeying the rules specified above regarding
substitution and overload resolution.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 03:32:53 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
This is less conservative and a little more "risky," but would allow us to
remove the std::promise<void> specialization without breaking most
user-code (unless, or course, the user code was relying on the signature of
set_value, such as taking its address). Compiler-folks are probably best at
understanding the implications of allowing empty meaning
default-constructed-void obeying the rules specified above regarding
substitution and overload resolution.
There is a also a more conservative approach that doesn't involve
specifying an empty argument to potentially be syntactic sugar for
default-constructed void: when removing the std::promise specialization for
void, give set_value a default-argument that is just default-constructed T.
This makes set_value easy to use without adding in the empty-argument
solution, but it means that set_value now also has a default argument. You
could use SFINAE to make it only default for void, but that's unfortunate
and would go back to the desire to special-case for void. Really it would
be great if it "just worked."
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 03:38:06 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Arthur O'Dwyer
Matt, what would you do about template type deduction in your system?
template<typename T> void g(T) { }
int main() {
g(); // valid or invalid?
g((void)0); // valid or invalid?
}
It depends. The most conservative approach is to make g() invalid, and
g((void)0) valid, deducing T as void. The downside of this is that it means
that calling set_value() on something like a promise<void> would require an
explicit expression. What probably makes the most sense is to instead have
an empty argument be allowed as syntactic sugar for default-constructed
void. This allows easy use of unary void functions from both generic and
non-generic code. In other words, you'd be able to invoke
std::promise<void>'s set_value (which would now actually take const void&
or void&&), by just calling set_value() without passing in an explicit
argument. The subtlety here is if there is an overload that actually is
nullary. In such a case, the unary-void overload should be considered and
substitution should occur, but the nullary function would be considered a
better match during overload resolution:]
Also, before being pointed out, yes, this has the potential to break code,
which is why it is the less conservative solution.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Arthur O'Dwyer
2015-08-04 06:55:30 UTC
Permalink
On Mon, Aug 3, 2015 at 8:24 PM, 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Arthur O'Dwyer
Matt, what would you do about template type deduction in your system?
template<typename T> void g(T) { }
int main() {
g(); // valid or invalid?
g((void)0); // valid or invalid?
}
It depends. The most conservative approach is to make g() invalid, and
g((void)0) valid, deducing T as void. The downside of this is that [it
doesn't solve the problem we want to solve, namely
std::promise<void>::set_value].
Notice that std::promise<void>::set_value(void) isn't exactly the same case
as what I'm talking about. In the set_value case, we merely need to allow a
parameter to have a type that is dependent yet void. C++ already allows
void types for non-dependently-typed parameters, so this should be
relatively simple. I'm talking about actually using type deduction to
*deduce* void for a parameter of *deduced* type.

However, even before we get there, I'm seeing repeatedly that you and I
disagree about the sensicality of a "unary function taking void".
In my world, that's impossible; a function whose argument list is
equivalent to void is nullary *by definition*.
And this is a good thing, because I *want* std::promise<void>::set_value() to
be a nullary function.

using Void = void;
// All three of these function signatures declare a nullary function.
// IMO we *must* keep this working.
void foo(Void);
void foo(void);
void foo();


using Void = void;
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
void foo() {} // foo0
void foo(Void) {} // foo1
In my world this is a violation of the ODR; foo0 and foo1 are the same
function.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
void bar() {} // bar0
template<class T>
void bar(T) {} // bar1
void baz(Void) {}
int main()
{
foo(); // calls foo0
foo(void()); // calls foo1
bar(); // calls bar0
bar(void()); // calls bar1, deducing T as void
The question I was asking in the previous message is: If bar0 was not
declared, would bar(); call bar1?
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
baz(); // calls baz
Of course; this has always been the case.

–Arthur
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Roland Bock
2015-08-04 07:32:24 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
using Void = void;
void foo() {} // foo0
void foo(Void) {} // foo1
In my world this is a violation of the ODR; foo0 and foo1 are the same
function.
Being aware that this is currently the case I wonder if it is a good idea?

If we could have values of type void, then I doubt that the above makes
sense?

How about binary functions?

void foo0(); // zero parameters
void foo1(void); // still zero parameters (you say)
void foo2(void, void); // ???

and how about

void foo2(int, void); // ???
void foo2(void, int); // ???

I would prefer all of them to be different, foo0 having zero parameters,
foo1 having one parameters, foo2 having two parameters.


Seems to me that this ship has sailed though. There is simply too much
code depending on foo0 being the same as foo1, I guess :-(


We could however introduce a new thing, e.g. std::none, that could be
designed from afresh and have all the nice features we would like to see
in void.

Regards,

Roland
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 08:02:32 UTC
Permalink
Post by Roland Bock
How about binary functions?
void foo0(); // zero parameters
void foo1(void); // still zero parameters (you say)
void foo2(void, void); // ???
and how about
void foo2(int, void); // ???
void foo2(void, int); // ???
I would prefer all of them to be different, foo0 having zero parameters,
foo1 having one parameters, foo2 having two parameters.
Yes!!! They are all logically different, we realistically just can't use
the syntax for foo1.

We could however introduce a new thing, e.g. std::none, that could be
Post by Roland Bock
designed from afresh and have all the nice features we would like to see in
void
Unfortunately that wouldn't help generic code unless everyone just updated
all of their code over night and nobody instantiated templates with void,
which realistically wouldn't happen. void is going to stay so ideally we
actually fix it in some way. IMO, we're really close to getting there. The
only thing we don't have IMHO is a good syntax for representing a unary
void function when void is not dependent. It's fine if there is an odd-ball
syntax for it because the dependent case is the one that comes up in
generic code. You wouldn't need to use the weird syntax there.

One possible solution -- we could have unary void functions declared as:

/////
void foo(explicit void) {}
/////

This is syntax that was illegal before, introduces no new keywords or
types, and doesn't look too obscure. There are probably many alternatives
and I'm not saying this is what it should be, we'd just need to pick one
that makes the most sense.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Roland Bock
2015-08-04 09:12:23 UTC
Permalink
On 2015-08-04 10:02, 'Matt Calabrese' via ISO C++ Standard - Future
Post by Roland Bock
How about binary functions?
void foo0(); // zero parameters
void foo1(void); // still zero parameters (you say)
void foo2(void, void); // ???
and how about
void foo2(int, void); // ???
void foo2(void, int); // ???
I would prefer all of them to be different, foo0 having zero
parameters, foo1 having one parameters, foo2 having two parameters.
Yes!!! They are all logically different, we realistically just can't
use the syntax for foo1.
We could however introduce a new thing, e.g. std::none, that could
be designed from afresh and have all the nice features we would
like to see in void
Unfortunately that wouldn't help generic code unless everyone just
updated all of their code over night and nobody instantiated templates
with void, which realistically wouldn't happen.
Right. Note to self: Do not write to this list too often, when suffering
from the flu.
Post by Roland Bock
void is going to stay so ideally we actually fix it in some way. IMO,
we're really close to getting there. The only thing we don't have IMHO
is a good syntax for representing a unary void function when void is
not dependent. It's fine if there is an odd-ball syntax for it because
the dependent case is the one that comes up in generic code. You
wouldn't need to use the weird syntax there.
/////
void foo(explicit void) {}
/////
Looks a bit weird, but sure looks like an option for solving the dilemma...
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Miro Knejp
2015-08-04 13:32:43 UTC
Permalink
Post by Roland Bock
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
/////
void foo(explicit void) {}
/////
Looks a bit weird, but sure looks like an option for solving the dilemma...
Except it requires modifying the callee instead of the caller, so it still won’t work with any existing code. Plus, how many people would remember to put that explicit in there? It is a manual opt-in to make other peoples’ generic code work with a function.

All of this really looks to me like we’re trying to make “void” be equivalent to the unit types in functional languages. There’s a lot of experience with those, so maybe that is a point to start doing research for C++ applicability. Yes, we already have unit types and one can define their own, but the point is that “void” currently is *not* a unit type and that’s what causes generic code headaches.

I don’t see it happening to have foo() and foo(void) define two different functions due to backwards compatibility.

Assuming for a moment that void were a unit type then it should be possible to create instances of it and call foo(void()) and therefore also foo(T()) if T=void. Unfortunately that breaks existing expression SFINAE as decltype(foo(T())) can be used to filter out deductions with T=void. Whether that is the best way to do it is pretty much irrelevant unless someone can show that it isn’t used in practice.

I know the pain of special casing void but unfortunately the term “the ship has sailed” is a term that comes up in these forums depressingly often. So I don’t think that the syntax void() will become possible under the assumption that the foo(T()) SFINAE is in use. One would probably require some type trait that is specialized for void and returns a magical type that can be used to call unary functions.

template<class T> struct babel_fish { using type = T; }
template<> struct babel_fish<void> { using type = /* dark magic*/; }

foo(babel_fish<T>());

I just hope we come up with a language solution because this is yet another load of library boilerplate that would need repeating *everywhere*.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Johannes Schaub' via ISO C++ Standard - Future Proposals
2015-08-04 09:40:09 UTC
Permalink
If we are already in oddballs mode, why not express the nondependent case
by dependent types

template<typename T = void>
void f(typename void_t<T>::type);

Am 04.08.2015 10:02 schrieb "'Matt Calabrese' via ISO C++ Standard - Future
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Roland Bock
How about binary functions?
void foo0(); // zero parameters
void foo1(void); // still zero parameters (you say)
void foo2(void, void); // ???
and how about
void foo2(int, void); // ???
void foo2(void, int); // ???
I would prefer all of them to be different, foo0 having zero parameters,
foo1 having one parameters, foo2 having two parameters.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Yes!!! They are all logically different, we realistically just can't use
the syntax for foo1.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Roland Bock
We could however introduce a new thing, e.g. std::none, that could be
designed from afresh and have all the nice features we would like to see in
void
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Unfortunately that wouldn't help generic code unless everyone just
updated all of their code over night and nobody instantiated templates with
void, which realistically wouldn't happen. void is going to stay so ideally
we actually fix it in some way. IMO, we're really close to getting there.
The only thing we don't have IMHO is a good syntax for representing a unary
void function when void is not dependent. It's fine if there is an odd-ball
syntax for it because the dependent case is the one that comes up in
generic code. You wouldn't need to use the weird syntax there.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
/////
void foo(explicit void) {}
/////
This is syntax that was illegal before, introduces no new keywords or
types, and doesn't look too obscure. There are probably many alternatives
and I'm not saying this is what it should be, we'd just need to pick one
that makes the most sense.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Johannes Schaub' via ISO C++ Standard - Future Proposals
2015-08-04 09:46:20 UTC
Permalink
Altermatively just give the void parameter a name. A named parameter
shouldn't catch the special case of meaning an empty parameter list.

Am 04.08.2015 11:40 schrieb "Johannes Schaub" <
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
If we are already in oddballs mode, why not express the nondependent case
by dependent types
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
template<typename T = void>
void f(typename void_t<T>::type);
Am 04.08.2015 10:02 schrieb "'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Roland Bock
How about binary functions?
void foo0(); // zero parameters
void foo1(void); // still zero parameters (you say)
void foo2(void, void); // ???
and how about
void foo2(int, void); // ???
void foo2(void, int); // ???
I would prefer all of them to be different, foo0 having zero
parameters, foo1 having one parameters, foo2 having two parameters.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Yes!!! They are all logically different, we realistically just can't
use the syntax for foo1.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Roland Bock
We could however introduce a new thing, e.g. std::none, that could be
designed from afresh and have all the nice features we would like to see in
void
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Unfortunately that wouldn't help generic code unless everyone just
updated all of their code over night and nobody instantiated templates with
void, which realistically wouldn't happen. void is going to stay so ideally
we actually fix it in some way. IMO, we're really close to getting there.
The only thing we don't have IMHO is a good syntax for representing a unary
void function when void is not dependent. It's fine if there is an odd-ball
syntax for it because the dependent case is the one that comes up in
generic code. You wouldn't need to use the weird syntax there.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
/////
void foo(explicit void) {}
/////
This is syntax that was illegal before, introduces no new keywords or
types, and doesn't look too obscure. There are probably many alternatives
and I'm not saying this is what it should be, we'd just need to pick one
that makes the most sense.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Edward Catmur' via ISO C++ Standard - Future Proposals
2015-08-04 09:50:24 UTC
Permalink
Yes! Also, deprecate (void) meaning () outside extern "C" - there's no need
for it, as C++ doesn't have unspecified parameter lists (even in C they're
obsolescent: 6.11.6 "Future language directions - Function declarators").

On Tue, Aug 4, 2015 at 10:46 AM, 'Johannes Schaub' via ISO C++ Standard -
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Altermatively just give the void parameter a name. A named parameter
shouldn't catch the special case of meaning an empty parameter list.
Am 04.08.2015 11:40 schrieb "Johannes Schaub" <
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
If we are already in oddballs mode, why not express the nondependent
case by dependent types
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
template<typename T = void>
void f(typename void_t<T>::type);
Am 04.08.2015 10:02 schrieb "'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Roland Bock
How about binary functions?
void foo0(); // zero parameters
void foo1(void); // still zero parameters (you say)
void foo2(void, void); // ???
and how about
void foo2(int, void); // ???
void foo2(void, int); // ???
I would prefer all of them to be different, foo0 having zero
parameters, foo1 having one parameters, foo2 having two parameters.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Yes!!! They are all logically different, we realistically just can't
use the syntax for foo1.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Roland Bock
We could however introduce a new thing, e.g. std::none, that could be
designed from afresh and have all the nice features we would like to see in
void
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Unfortunately that wouldn't help generic code unless everyone just
updated all of their code over night and nobody instantiated templates with
void, which realistically wouldn't happen. void is going to stay so ideally
we actually fix it in some way. IMO, we're really close to getting there.
The only thing we don't have IMHO is a good syntax for representing a unary
void function when void is not dependent. It's fine if there is an odd-ball
syntax for it because the dependent case is the one that comes up in
generic code. You wouldn't need to use the weird syntax there.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
One possible solution -- we could have unary void functions declared
/////
void foo(explicit void) {}
/////
This is syntax that was illegal before, introduces no new keywords or
types, and doesn't look too obscure. There are probably many alternatives
and I'm not saying this is what it should be, we'd just need to pick one
that makes the most sense.
--
---
You received this message because you are subscribed to a topic in the
Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit
https://groups.google.com/a/isocpp.org/d/topic/std-proposals/05prNzycvYU/unsubscribe
.
To unsubscribe from this group and all its topics, send an email to
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Magnus Fromreide
2015-08-04 21:05:19 UTC
Permalink
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
Yes! Also, deprecate (void) meaning () outside extern "C" - there's no need
for it, as C++ doesn't have unspecified parameter lists (even in C they're
obsolescent: 6.11.6 "Future language directions - Function declarators").
I would expect this to needlessly introduce compilation erros in quite a few
code bases and are strongly opposed to it in any near time, but for the
presumed C++4x it might be a reasonable idea...

/MF
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
On Tue, Aug 4, 2015 at 10:46 AM, 'Johannes Schaub' via ISO C++ Standard -
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Altermatively just give the void parameter a name. A named parameter
shouldn't catch the special case of meaning an empty parameter list.
Am 04.08.2015 11:40 schrieb "Johannes Schaub" <
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
If we are already in oddballs mode, why not express the nondependent
case by dependent types
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
template<typename T = void>
void f(typename void_t<T>::type);
Am 04.08.2015 10:02 schrieb "'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Roland Bock
How about binary functions?
void foo0(); // zero parameters
void foo1(void); // still zero parameters (you say)
void foo2(void, void); // ???
and how about
void foo2(int, void); // ???
void foo2(void, int); // ???
I would prefer all of them to be different, foo0 having zero
parameters, foo1 having one parameters, foo2 having two parameters.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Yes!!! They are all logically different, we realistically just can't
use the syntax for foo1.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Roland Bock
We could however introduce a new thing, e.g. std::none, that could be
designed from afresh and have all the nice features we would like to see in
void
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Unfortunately that wouldn't help generic code unless everyone just
updated all of their code over night and nobody instantiated templates with
void, which realistically wouldn't happen. void is going to stay so ideally
we actually fix it in some way. IMO, we're really close to getting there.
The only thing we don't have IMHO is a good syntax for representing a unary
void function when void is not dependent. It's fine if there is an odd-ball
syntax for it because the dependent case is the one that comes up in
generic code. You wouldn't need to use the weird syntax there.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
One possible solution -- we could have unary void functions declared
/////
void foo(explicit void) {}
/////
This is syntax that was illegal before, introduces no new keywords or
types, and doesn't look too obscure. There are probably many alternatives
and I'm not saying this is what it should be, we'd just need to pick one
that makes the most sense.
--
---
You received this message because you are subscribed to a topic in the
Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit
https://groups.google.com/a/isocpp.org/d/topic/std-proposals/05prNzycvYU/unsubscribe
.
To unsubscribe from this group and all its topics, send an email to
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Arthur O'Dwyer
2015-08-04 08:05:30 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
using Void = void;
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
void foo() {} // foo0
void foo(Void) {} // foo1
In my world this is a violation of the ODR; foo0 and foo1 are the same
function.
Being aware that this is currently the case I wonder if it is a good idea?
If we could have values of type void, then I doubt that the above makes
sense?
[...]
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
We could however introduce a new thing, e.g. std::none, that could be
designed from afresh and have all the nice features we would like to see in
void.
Sounds like std::nullptr_t is already *that* thing: it's new, it's unitary,
it provides relational operators. If you just want something that behaves
like Python's None, nullptr fits the bill exactly.

Another new-in-C++11, unitary, regular type in the standard library is
std::tuple<> (that is, a tuple of zero elements).

But the problem is generic programming that needs to work with void
parameter types (a.k.a. nullary functions) and void return types — not
nullptr_t, not std::tuple<>, but literally and exactly void. Inventing new
types almost certainly won't solve the problem, because the problem
specifically concerns void.

–Arthur
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Jared Grubb
2015-08-04 15:04:52 UTC
Permalink
Post by Roland Bock
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
using Void = void;
void foo() {} // foo0
void foo(Void) {} // foo1
In my world this is a violation of the ODR; foo0 and foo1 are the same function.
Being aware that this is currently the case I wonder if it is a good idea?
If we could have values of type void, then I doubt that the above makes sense?
[...]
We could however introduce a new thing, e.g. std::none, that could be designed from afresh and have all the nice features we would like to see in void.
Sounds like std::nullptr_t is already that thing: it's new, it's unitary, it provides relational operators. If you just want something that behaves like Python's None, nullptr fits the bill exactly.
The issue there is that is that nullptr is convertible to lots of things (T*) and not to a many others (T). That makes it an odd universal void type.
Post by Roland Bock
Another new-in-C++11, unitary, regular type in the standard library is std::tuple<> (that is, a tuple of zero elements).
But the problem is generic programming that needs to work with void parameter types (a.k.a. nullary functions) and void return types — not nullptr_t, not std::tuple<>, but literally and exactly void. Inventing new types almost certainly won't solve the problem, because the problem specifically concerns void.
Maybe there should be a new special type defined as decltype(void), which is convertible to and from places where ‘void’ currently exists, but is also a regular type. It can be a “conduit” type to make void things regular, but give you an escape hatch back to ‘void’ when it’s needed.

auto v = foo(); // type is ‘decltype(void)’
return v; // ‘decltype(void)’ is convertible to void return type

We define a name under namespace std that is defined to be that type (like nullptr_t does).
Post by Roland Bock
–Arthur
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/ <http://groups.google.com/a/isocpp.org/group/std-proposals/>.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 07:44:21 UTC
Permalink
Post by Arthur O'Dwyer
Notice that std::promise<void>::set_value(void) isn't exactly the same
case as what I'm talking about. In the set_value case, we merely need to
allow a parameter to have a type that is dependent yet void. C++ already
allows void types for non-dependently-typed parameters, so this should be
relatively simple. I'm talking about actually using type deduction to
*deduce* void for a parameter of *deduced* type.
I know the case you are talking about -- I already explained how it'd be
handled, I'm just trying to show all cases for clarity and to show its
consistency with the non-deduced version. I don't believe that we should
change one without changing the other. Ideally they are consistent with one
another.

However, even before we get there, I'm seeing repeatedly that you and I
Post by Arthur O'Dwyer
disagree about the sensicality of a "unary function taking void".
In my world, that's impossible; a function whose argument list is
equivalent to void is nullary *by definition*.
Then you are being inconsistent in what you think void means. You've
already stated that you understand what tuple<int, void> is and it is
consistent with what I've stated. This effectively implies that you accept
unary void is a sensible construct. To be clear on why that is, simply
imagine a make_tuple-like facility that takes the arguments by value. What
do you think the signature of that function is for a tuple<int, int>? What
do you think the signature is for tuple<int, void, int>? What about
tuple<void>? What about tuple<>? Note that the last two cases are
*very* different.
The former is a unary function that takes void. The latter is a true,
nullary function. They are in no way the same thing. Do not flatten unary
void to nullary. They are distinct.
Post by Arthur O'Dwyer
And this is a good thing, because I *want* std::promise<void>::set_value() to
be a nullary function.
using Void = void;
// All three of these function signatures declare a nullary function.
// IMO we *must* keep this working.
void foo(Void);
void foo(void);
void foo();
My mistake -- I was thinking that declarations using a typedef of void were
currently illegal, but that is only the case if the typedef was dependent.
I agree that this particular syntax shouldn't change meaning, then.
However, regardless of what the syntax is in the non-dependent context,
there still needs to be a way to declare such a function. However it is to
be declared, the behavior shown is what we should expect. It's just less
than ideal that we can't use the natural syntax for it in the unary case (a
function taking a single void parameter). When the type is dependent, which
is the case we are trying to fix for generic code, this would work directly.

The question I was asking in the previous message is: If bar0 was not
Post by Arthur O'Dwyer
declared, would bar(); call bar1?
Yes, as I described, that overload would be considered and go through
substitution. The nullary version is only picked because it is a better
match. If the nullary version weren't there, then bar1 would be picked.
Post by Arthur O'Dwyer
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
baz(); // calls baz
Of course; this has always been the case.
Again, I'm just enumerating the relevant cases. Pretend this is the
hypothetical unary void case (as opposed to the nullary case), however it
would actually be declared.
Post by Arthur O'Dwyer
On Mon, Aug 3, 2015 at 8:24 PM, 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Arthur O'Dwyer
Matt, what would you do about template type deduction in your system?
template<typename T> void g(T) { }
int main() {
g(); // valid or invalid?
g((void)0); // valid or invalid?
}
It depends. The most conservative approach is to make g() invalid, and
g((void)0) valid, deducing T as void. The downside of this is that [it
doesn't solve the problem we want to solve, namely
std::promise<void>::set_value].
Notice that std::promise<void>::set_value(void) isn't exactly the same
case as what I'm talking about. In the set_value case, we merely need to
allow a parameter to have a type that is dependent yet void. C++ already
allows void types for non-dependently-typed parameters, so this should be
relatively simple. I'm talking about actually using type deduction to
*deduce* void for a parameter of *deduced* type.
However, even before we get there, I'm seeing repeatedly that you and I
disagree about the sensicality of a "unary function taking void".
In my world, that's impossible; a function whose argument list is
equivalent to void is nullary *by definition*.
And this is a good thing, because I *want* std::promise<void>::set_value() to
be a nullary function.
using Void = void;
// All three of these function signatures declare a nullary function.
// IMO we *must* keep this working.
void foo(Void);
void foo(void);
void foo();
using Void = void;
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
void foo() {} // foo0
void foo(Void) {} // foo1
In my world this is a violation of the ODR; foo0 and foo1 are the same
function.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
void bar() {} // bar0
template<class T>
void bar(T) {} // bar1
void baz(Void) {}
int main()
{
foo(); // calls foo0
foo(void()); // calls foo1
bar(); // calls bar0
bar(void()); // calls bar1, deducing T as void
The question I was asking in the previous message is: If bar0 was not
declared, would bar(); call bar1?
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
baz(); // calls baz
Of course; this has always been the case.
–Arthur
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Arthur O'Dwyer
2015-08-04 08:25:29 UTC
Permalink
On Tue, Aug 4, 2015 at 12:44 AM, 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Arthur O'Dwyer
Notice that std::promise<void>::set_value(void) isn't exactly the same
case as what I'm talking about. In the set_value case, we merely need to
allow a parameter to have a type that is dependent yet void. C++ already
allows void types for non-dependently-typed parameters, so this should be
relatively simple. I'm talking about actually using type deduction to
*deduce* void for a parameter of *deduced* type.
I know the case you are talking about -- I already explained how it'd be
handled, I'm just trying to show all cases for clarity and to show its
consistency with the non-deduced version. I don't believe that we should
change one without changing the other. Ideally they are consistent with one
another.
However, even before we get there, I'm seeing repeatedly that you and I
Post by Arthur O'Dwyer
disagree about the sensicality of a "unary function taking void".
In my world, that's impossible; a function whose argument list is
equivalent to void is nullary *by definition*.
Then you are being inconsistent in what you think void means. You've
already stated that you understand what tuple<int, void> is and it is
consistent with what I've stated. This effectively implies that you accept
unary void is a sensible construct.
Not really. It means I see what you mean by tuple<int,void> — you mean an
object t such that std::tuple_size(t) returns 2, std::get<0>(t) returns an
int and std::get<1>(t) returns nothing. That much isn't controversial to me.
I understand that you want to be able to construct such an object via
std::make_tuple(1,
void()). I consider *that* part controversial.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
To be clear on why that is, simply imagine a make_tuple-like facility that
takes the arguments by value. What do you think the signature of that
function is for a tuple<int, int>? What do you think the signature is for
tuple<int, void, int>? What about tuple<void>? What about tuple<>? Note
that the last two cases are *very* different. The former is a unary
function that takes void. The latter is a true, nullary function. They are
in no way the same thing. Do not flatten unary void to nullary. They are
distinct.
I agree that std::tuple<void>, if it were allowed, would be a different
type from std::tuple<>, in exactly the same way that boost::mpl::list<void>
is a different type from boost::mpl::list<>.
However, int(*)(void) and int(*)() denote the same type; always have,
always will, and IMO always should.
I don't know how to reconcile std::tuple<void> with make_tuple. I don't
think that introducing a new "unary void" is the right way to do it.
Instead of introducing "unary void", I would rather leave std::tuple<void>
ill-formed, or even say that it is well-formed but that instances of it
cannot be constructed via any kind of make_tuple.

–Arthur
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Bengt Gustafsson
2015-08-04 09:00:41 UTC
Permalink
How about just defining that void behaves like an empty struct? This avoids
us having to discuss every particular case. There are still the potentials
for code breakage with tricky SFINAE but I don't see how that can ever be
avoided if we change the behaviour of void at all. Note: I don't mean that
void should BE an empty struct in that you can inherit from it, but that it
acts like one when used in code.
Post by Arthur O'Dwyer
On Tue, Aug 4, 2015 at 12:44 AM, 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Arthur O'Dwyer
Notice that std::promise<void>::set_value(void) isn't exactly the same
case as what I'm talking about. In the set_value case, we merely need to
allow a parameter to have a type that is dependent yet void. C++ already
allows void types for non-dependently-typed parameters, so this should be
relatively simple. I'm talking about actually using type deduction to
*deduce* void for a parameter of *deduced* type.
I know the case you are talking about -- I already explained how it'd be
handled, I'm just trying to show all cases for clarity and to show its
consistency with the non-deduced version. I don't believe that we should
change one without changing the other. Ideally they are consistent with one
another.
However, even before we get there, I'm seeing repeatedly that you and I
Post by Arthur O'Dwyer
disagree about the sensicality of a "unary function taking void".
In my world, that's impossible; a function whose argument list is
equivalent to void is nullary *by definition*.
Then you are being inconsistent in what you think void means. You've
already stated that you understand what tuple<int, void> is and it is
consistent with what I've stated. This effectively implies that you accept
unary void is a sensible construct.
Not really. It means I see what you mean by tuple<int,void> — you mean an
object t such that std::tuple_size(t) returns 2, std::get<0>(t) returns
an int and std::get<1>(t) returns nothing. That much isn't controversial
to me.
I understand that you want to be able to construct such an object via std::make_tuple(1,
void()). I consider *that* part controversial.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
To be clear on why that is, simply imagine a make_tuple-like facility
that takes the arguments by value. What do you think the signature of that
function is for a tuple<int, int>? What do you think the signature is for
tuple<int, void, int>? What about tuple<void>? What about tuple<>? Note
that the last two cases are *very* different. The former is a unary
function that takes void. The latter is a true, nullary function. They are
in no way the same thing. Do not flatten unary void to nullary. They are
distinct.
I agree that std::tuple<void>, if it were allowed, would be a different
type from std::tuple<>, in exactly the same way that
boost::mpl::list<void> is a different type from boost::mpl::list<>.
However, int(*)(void) and int(*)() denote the same type; always have,
always will, and IMO always should.
I don't know how to reconcile std::tuple<void> with make_tuple. I don't
think that introducing a new "unary void" is the right way to do it.
Instead of introducing "unary void", I would rather leave std::tuple<void>
ill-formed, or even say that it is well-formed but that instances of it
cannot be constructed via any kind of make_tuple.
–Arthur
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 09:14:33 UTC
Permalink
On Tue, Aug 4, 2015 at 2:00 AM, Bengt Gustafsson <
Post by Bengt Gustafsson
How about just defining that void behaves like an empty struct? This
avoids us having to discuss every particular case. There are still the
potentials for code breakage with tricky SFINAE but I don't see how that
can ever be avoided if we change the behaviour of void at all.
Thank you, this is what I'm saying! I don't even really care about the
syntactic sugar regarding construction if it's considered too magical or
prone to more breakage. Just having void be equivalent to an empty struct
that is regular is all that is necessary to have it work in generic code
and eliminate special casing. I don't understand the hang up on this.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Paul Fultz II
2015-08-04 14:57:44 UTC
Permalink
Post by Bengt Gustafsson
How about just defining that void behaves like an empty struct? This
avoids us having to discuss every particular case. There are still the
potentials for code breakage with tricky SFINAE but I don't see how that
can ever be avoided if we change the behaviour of void at all. Note: I
don't mean that void should BE an empty struct in that you can inherit from
it, but that it acts like one when used in code.
But having void act like an empty struct, then it becomes a type with one
value. However, `void` should mean a type with zero values(like a function
that doesn't return anything). Of course, changing `void` from having zero
values to one value could break a lot of assumptions made about code. I
think its a bad idea. For some generic code, it may be safe to assume void
as having one value instead of zero, but it would be better to use a
library solution for this instead of baking in potential unsafe type
assumptions into the language.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Zach Laine
2015-08-04 15:13:04 UTC
Permalink
Post by Paul Fultz II
Post by Bengt Gustafsson
How about just defining that void behaves like an empty struct? This
avoids us having to discuss every particular case. There are still the
potentials for code breakage with tricky SFINAE but I don't see how that
can ever be avoided if we change the behaviour of void at all. Note: I
don't mean that void should BE an empty struct in that you can inherit from
it, but that it acts like one when used in code.
But having void act like an empty struct, then it becomes a type with one
value. However, `void` should mean a type with zero values(like a function
that doesn't return anything). Of course, changing `void` from having zero
values to one value could break a lot of assumptions made about code. I
think its a bad idea. For some generic code, it may be safe to assume void
as having one value instead of zero, but it would be better to use a
library solution for this instead of baking in potential unsafe type
assumptions into the language.
Again, I don't see the one value you mention. Please point to the value
below:

struct foo
{
};

Where is it? Objects of type "foo" have an address, but no value, right?
What am I missing?

Zach
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Edward Catmur' via ISO C++ Standard - Future Proposals
2015-08-04 15:15:47 UTC
Permalink
Here is the value:

foo{}
[...] 9 - The void type has an empty set of values. [...]
An expression of type void *does not have a value*.
Post by Paul Fultz II
Post by Bengt Gustafsson
How about just defining that void behaves like an empty struct? This
avoids us having to discuss every particular case. There are still the
potentials for code breakage with tricky SFINAE but I don't see how that
can ever be avoided if we change the behaviour of void at all. Note: I
don't mean that void should BE an empty struct in that you can inherit from
it, but that it acts like one when used in code.
But having void act like an empty struct, then it becomes a type with one
value. However, `void` should mean a type with zero values(like a function
that doesn't return anything). Of course, changing `void` from having zero
values to one value could break a lot of assumptions made about code. I
think its a bad idea. For some generic code, it may be safe to assume void
as having one value instead of zero, but it would be better to use a
library solution for this instead of baking in potential unsafe type
assumptions into the language.
Again, I don't see the one value you mention. Please point to the value
struct foo
{
};
Where is it? Objects of type "foo" have an address, but no value, right?
What am I missing?
Zach
--
---
You received this message because you are subscribed to a topic in the
Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit
https://groups.google.com/a/isocpp.org/d/topic/std-proposals/05prNzycvYU/unsubscribe
.
To unsubscribe from this group and all its topics, send an email to
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Johannes Schaub' via ISO C++ Standard - Future Proposals
2015-08-04 16:37:40 UTC
Permalink
Am 04.08.2015 17:15 schrieb "'Edward Catmur' via ISO C++ Standard - Future
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
foo{}
[...] 9 - The void type has an empty set of values. [...]
An expression of type void *does not have a value*.
While I agree, I must point out that there does not appear to be consensus
(even officially) on the definition of the term "value". While the spec has
a seemingly clear definition of it, with the introduction of the terms
"value computation" that can result in an object or function identity for
lvalues and in member function identity for rvalues, the term "value" has
been used by some in manners meaning "result meaning of expression
evaluation".
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
Post by Paul Fultz II
Post by Bengt Gustafsson
How about just defining that void behaves like an empty struct? This
avoids us having to discuss every particular case. There are still the
potentials for code breakage with tricky SFINAE but I don't see how that
can ever be avoided if we change the behaviour of void at all. Note: I
don't mean that void should BE an empty struct in that you can inherit from
it, but that it acts like one when used in code.
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
Post by Paul Fultz II
But having void act like an empty struct, then it becomes a type with
one value. However, `void` should mean a type with zero values(like a
function that doesn't return anything). Of course, changing `void` from
having zero values to one value could break a lot of assumptions made about
code. I think its a bad idea. For some generic code, it may be safe to
assume void as having one value instead of zero, but it would be better to
use a library solution for this instead of baking in potential unsafe type
assumptions into the language.
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
Again, I don't see the one value you mention. Please point to the value
struct foo
{
};
Where is it? Objects of type "foo" have an address, but no value,
right? What am I missing?
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
Zach
--
---
You received this message because you are subscribed to a topic in the
Google Groups "ISO C++ Standard - Future Proposals" group.
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
To unsubscribe from this topic, visit
https://groups.google.com/a/isocpp.org/d/topic/std-proposals/05prNzycvYU/unsubscribe
.
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
To unsubscribe from this group and all its topics, send an email to
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-08-04 15:24:52 UTC
Permalink
Post by Zach Laine
Again, I don't see the one value you mention. Please point to the value
struct foo
{
};
Where is it? Objects of type "foo" have an address, but no value, right?
What am I missing?
It took me some time to understand Matt's proposition too... there's a very
subtle difference between types with zero states and types with one state.

An empty struct, just like std::nullptr_t, can only assume one state. You can
say the state is "exists" or "created". Compare this to bool, which has two
states and a tristate which has, as its name says, three states.

A zero-state type requires a jump, a leap of faith. Remember when you were
sometime in early school and discussed zero: how can you have zero of
something? I don't remember the details of the discussion, but I remember
having discussions about it because it's non-obvious, like negative numbers
aren't obvious other.

Having zero of something is the same as not having. So an object with zero
states is the same as the object not existing. It does not even have an
address.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Zach Laine
2015-08-04 15:36:03 UTC
Permalink
Post by Thiago Macieira
Post by Zach Laine
Again, I don't see the one value you mention. Please point to the value
struct foo
{
};
Where is it? Objects of type "foo" have an address, but no value, right?
What am I missing?
It took me some time to understand Matt's proposition too... there's a very
subtle difference between types with zero states and types with one state.
An empty struct, just like std::nullptr_t, can only assume one state. You can
say the state is "exists" or "created". Compare this to bool, which has two
states and a tristate which has, as its name says, three states.
A zero-state type requires a jump, a leap of faith. Remember when you were
sometime in early school and discussed zero: how can you have zero of
something? I don't remember the details of the discussion, but I remember
having discussions about it because it's non-obvious, like negative numbers
aren't obvious other.
Having zero of something is the same as not having. So an object with zero
states is the same as the object not existing. It does not even have an
address.
I'm a bit confused by your response. IFAICT, Matt is suggesting that void
behave as if it were defined as struct void{}. Right?

Zach
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-08-04 15:41:44 UTC
Permalink
Post by Zach Laine
I'm a bit confused by your response. IFAICT, Matt is suggesting that void
behave as if it were defined as struct void{}. Right?
I thought he was, until the discussion on std::tuple<int, void, int>.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 18:36:03 UTC
Permalink
Post by Thiago Macieira
Post by Zach Laine
I'm a bit confused by your response. IFAICT, Matt is suggesting that
void
Post by Zach Laine
behave as if it were defined as struct void{}. Right?
I thought he was, until the discussion on std::tuple<int, void, int>.
What did I say that caused the confusion there? I still mean struct void
{}; (and the Regular overloads). Refer to the definition I gave at the
start of all of this. tuple behavior I described should not go against that
and if I specified otherwise somewhere then I was in error.

To be absolutely clear, std::tuple<int, void, int> is just a tuple with 3
elements: an int, a void, and an int. a make_tuple invocation takes 3
parameters -- an int, a void, and an int (technically references). You
would invoke it, usually in generic code, just as anything else:
make_tuple(int_fun(), void_fun(), int_fun()). I further suggest that we may
want to consider supporting syntactic sugar for constructing void, which
would be a whitespace argument, in an attempt to bridge the gap when people
update functions to now take a single void parameter, though currently do
not (promise's set_value(), for example). In other words, you could
equivalently call make_tuple(int_fun(), , int_fun()). The reason why this
is somewhat compelling is it means that you can allow the nullary and unary
void case to be invoked the same way, and just avoid ambiguities by
preferring the nullary overload as a better match if there are multiple
candidates. This syntactic sugar is at least a little bit risky, because it
has some chance of breaking old code (particularly due to the potential
need for an overload to go through substitution that previously wan't even
considered, and a non-SFINAE failure can occur during that substitution
process, or simply templates may be prematurely instantiated, which can
legitimately alter meaning and behavior of the program).

To explain why this might be something we'd want to at least consider, go
back to std::promise<void>. If we remove the separate specialization and
instead rely on the default definition, set_value() would now have one
version that takes a void&& and one version that takes a const void&.
However, in real-world code today, people who call set_value() of
std::promise<void> do not pass anything other than the promise itself (as
this). This means that if we simply removed the specialization, it would
definitely break code. If we support the notion of "empty argument
potentially means void()", then this wouldn't break. std::promise is one
example here, but real-world generic code also has specializations like
these. If we want to make minimal breakages in code, this might help, but
it is actually a trade-off in potential changes and isn't strictly sound.
The conservative approach would be to just force people to update their
code, since it doesn't muck with overload resolution, but I'm just throwing
the idea out in the open because it's at least somewhat compelling and the
effect in practice is that less user code would need to be updated. If this
is the part that was causing the confusion, then let me know if it's not
more clear.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 17:59:14 UTC
Permalink
Post by Thiago Macieira
Post by Zach Laine
Again, I don't see the one value you mention. Please point to the value
struct foo
{
};
Where is it? Objects of type "foo" have an address, but no value, right?
What am I missing?
It took me some time to understand Matt's proposition too... there's a very
subtle difference between types with zero states and types with one state.
In case there is any confusion, I DO mean struct void {}; I defined in C++
the type in a couple of replies in a hope that it would eliminate all
confusion. If "stateless" is the confusing part, for clarity, I'm referring
to the boost definition of stateless -- the docs have always had a standard
reference, though it now appears outdated. I didn't think there would be
confusion using this term in this forum. Quoting the docs:

//////////
A stateless type is a type that has no storage and whose constructors and
destructors are trivial. That means that is_stateless only inherits from
true_type if the following expression is true:

::boost::has_trivial_constructor<T>::value
&& ::boost::has_trivial_copy<T>::value
&& ::boost::has_trivial_destructor<T>::value
&& ::boost::is_class<T>::value
&& ::boost::is_empty<T>::value
//////////
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-08-04 18:30:44 UTC
Permalink
On Tuesday 04 August 2015 10:59:14 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
In case there is any confusion, I DO mean struct void {}; I defined in C++
the type in a couple of replies in a hope that it would eliminate all
confusion
You've failed in eliminating confusion.

Please choose one only:

a) struct void {};
b) zero states
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 18:47:39 UTC
Permalink
Post by Thiago Macieira
On Tuesday 04 August 2015 10:59:14 'Matt Calabrese' via ISO C++ Standard -
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
In case there is any confusion, I DO mean struct void {}; I defined in
C++
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
the type in a couple of replies in a hope that it would eliminate all
confusion
You've failed in eliminating confusion.
a) struct void {};
b) zero states
I think that did clarify, but to be very clear, it's more akin to (a)
without actually being a user-defined type. I'm using actual type
definitions and standard terms in replies so that we don't have to go into
the semantics of this stuff. If there is some ambiguity in terms, prefer
the c++ standard meaning.

So, restated, I absolutely do not mean that void should be something less
than or different from a complete type in C++. It should just be equivalent
to a type with no members that is Regular. This should not be a new kind of
entity for the language with its own semantics, as that would not aid in
writing generic code. It would mean more special casing. All that we need
for void is for it to be a regular type.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Paul Fultz II
2015-08-04 15:32:14 UTC
Permalink
Post by Zach Laine
Post by Paul Fultz II
Post by Bengt Gustafsson
How about just defining that void behaves like an empty struct? This
avoids us having to discuss every particular case. There are still the
potentials for code breakage with tricky SFINAE but I don't see how that
can ever be avoided if we change the behaviour of void at all. Note: I
don't mean that void should BE an empty struct in that you can inherit from
it, but that it acts like one when used in code.
But having void act like an empty struct, then it becomes a type with one
value. However, `void` should mean a type with zero values(like a function
that doesn't return anything). Of course, changing `void` from having zero
values to one value could break a lot of assumptions made about code. I
think its a bad idea. For some generic code, it may be safe to assume void
as having one value instead of zero, but it would be better to use a
library solution for this instead of baking in potential unsafe type
assumptions into the language.
Again, I don't see the one value you mention. Please point to the value
struct foo
{
};
Where is it? Objects of type "foo" have an address, but no value, right?
What am I missing?
Its `foo()`. Thats the one value. You can assign it to a variable:

auto x = foo();

So what value does `x` have? It has to have a value, and it is `foo()`.
Futhermore, you can return its value from a function:

foo f()
{ return foo(); }

If `foo` didn't have a value, then we wouldn't be able to return it from a
function. Also, you can observe that the values start to add up when we
start to sum them using a variant. How many values does `foo` have now?

struct empty1 {};
struct empty2 {};

typedef variant<empty1, empty2> foo;

It has two: `empty1()` and `empty2()`.
Post by Zach Laine
Zach
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Zach Laine
2015-08-04 15:41:05 UTC
Permalink
Post by Paul Fultz II
Post by Zach Laine
Post by Paul Fultz II
Post by Bengt Gustafsson
How about just defining that void behaves like an empty struct? This
avoids us having to discuss every particular case. There are still the
potentials for code breakage with tricky SFINAE but I don't see how that
can ever be avoided if we change the behaviour of void at all. Note: I
don't mean that void should BE an empty struct in that you can inherit from
it, but that it acts like one when used in code.
But having void act like an empty struct, then it becomes a type with
one value. However, `void` should mean a type with zero values(like a
function that doesn't return anything). Of course, changing `void` from
having zero values to one value could break a lot of assumptions made about
code. I think its a bad idea. For some generic code, it may be safe to
assume void as having one value instead of zero, but it would be better to
use a library solution for this instead of baking in potential unsafe type
assumptions into the language.
Again, I don't see the one value you mention. Please point to the value
struct foo
{
};
Where is it? Objects of type "foo" have an address, but no value,
right? What am I missing?
auto x = foo();
So what value does `x` have? It has to have a value, and it is `foo()`.
foo f()
{ return foo(); }
If `foo` didn't have a value, then we wouldn't be able to return it from a
function. Also, you can observe that the values start to add up when we
start to sum them using a variant. How many values does `foo` have now?
struct empty1 {};
struct empty2 {};
typedef variant<empty1, empty2> foo;
It has two: `empty1()` and `empty2()`.
Please write an if statement predicated on the values of empty1() and
empty2() that takes different paths depending on runtime state, without
relying on UB.

My point is that there is no non-UB-invoking observable runtime state for
these objects. They have no values that are meaningful to my program's
state. They are meaningful at compile time only.

Zach
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Paul Fultz II
2015-08-04 16:14:04 UTC
Permalink
Post by Zach Laine
Post by Paul Fultz II
Post by Zach Laine
Post by Paul Fultz II
Post by Bengt Gustafsson
How about just defining that void behaves like an empty struct? This
avoids us having to discuss every particular case. There are still the
potentials for code breakage with tricky SFINAE but I don't see how that
can ever be avoided if we change the behaviour of void at all. Note: I
don't mean that void should BE an empty struct in that you can inherit from
it, but that it acts like one when used in code.
But having void act like an empty struct, then it becomes a type with
one value. However, `void` should mean a type with zero values(like a
function that doesn't return anything). Of course, changing `void` from
having zero values to one value could break a lot of assumptions made about
code. I think its a bad idea. For some generic code, it may be safe to
assume void as having one value instead of zero, but it would be better to
use a library solution for this instead of baking in potential unsafe type
assumptions into the language.
Again, I don't see the one value you mention. Please point to the value
struct foo
{
};
Where is it? Objects of type "foo" have an address, but no value,
right? What am I missing?
auto x = foo();
So what value does `x` have? It has to have a value, and it is `foo()`.
foo f()
{ return foo(); }
If `foo` didn't have a value, then we wouldn't be able to return it from
a function. Also, you can observe that the values start to add up when we
start to sum them using a variant. How many values does `foo` have now?
struct empty1 {};
struct empty2 {};
typedef variant<empty1, empty2> foo;
It has two: `empty1()` and `empty2()`.
Please write an if statement predicated on the values of empty1() and
empty2() that takes different paths depending on runtime state, without
relying on UB.
foo x = empty1();

if (x.which() == 0) std::cout << "empty1";
else std::cout << "empty2";

Thats not UB. As someone else suggested that `variant` have a templated
`has` method, you could write this:

if (x.has<empty1>()) std::cout << "empty1";
else std::cout << "empty2";

Same logic, just easier to read.
Post by Zach Laine
My point is that there is no non-UB-invoking observable runtime state for
these objects. They have no values that are meaningful to my program's
state. They are meaningful at compile time only.
I am discussing runtime values,not compile time values. In fact, `foo` has
the same runtime values that a boolean has, with the addition of being able
to pattern match on its values. The fact that runtime values and compile
time values are the same is because it is dependently-typed.
Post by Zach Laine
Zach
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Zach Laine
2015-08-04 16:54:32 UTC
Permalink
Post by Paul Fultz II
Post by Zach Laine
Post by Paul Fultz II
auto x = foo();
So what value does `x` have? It has to have a value, and it is `foo()`.
foo f()
{ return foo(); }
If `foo` didn't have a value, then we wouldn't be able to return it from
a function. Also, you can observe that the values start to add up when we
start to sum them using a variant. How many values does `foo` have now?
struct empty1 {};
struct empty2 {};
typedef variant<empty1, empty2> foo;
It has two: `empty1()` and `empty2()`.
Please write an if statement predicated on the values of empty1() and
empty2() that takes different paths depending on runtime state, without
relying on UB.
foo x = empty1();
if (x.which() == 0) std::cout << "empty1";
Stop right there. You are using the value of the discriminator
"x.which()", which is an int with a runtime value. You never used the
values of empty1() and empty2().

Zach
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-08-04 18:31:58 UTC
Permalink
Post by Zach Laine
Stop right there. You are using the value of the discriminator
"x.which()", which is an int with a runtime value. You never used the
values of empty1() and empty2().
There's no need to, because they can only assume one possible value.

It's like checking whether a value of type std::nullptr_t is null. It is, you
don't have to check.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Zach Laine
2015-08-04 20:58:34 UTC
Permalink
Post by Thiago Macieira
Post by Zach Laine
Stop right there. You are using the value of the discriminator
"x.which()", which is an int with a runtime value. You never used the
values of empty1() and empty2().
There's no need to, because they can only assume one possible value.
It's like checking whether a value of type std::nullptr_t is null. It is, you
don't have to check.
That is precisely my point. This discussion started with Paul's statement
that use of the "struct void{}:" definition injects extra state into your
program that was not there before. I haven't yet seen it.

Zach
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Edward Catmur' via ISO C++ Standard - Future Proposals
2015-08-04 22:30:46 UTC
Permalink
Previously a variant<bool, void> could have two values: true or false. Now
it can have three values: true, false or void{}. There's your extra state.
Post by Zach Laine
Post by Thiago Macieira
Post by Zach Laine
Stop right there. You are using the value of the discriminator
"x.which()", which is an int with a runtime value. You never used the
values of empty1() and empty2().
There's no need to, because they can only assume one possible value.
It's like checking whether a value of type std::nullptr_t is null. It is, you
don't have to check.
That is precisely my point. This discussion started with Paul's statement
that use of the "struct void{}:" definition injects extra state into your
program that was not there before. I haven't yet seen it.
Zach
--
---
You received this message because you are subscribed to a topic in the
Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit
https://groups.google.com/a/isocpp.org/d/topic/std-proposals/05prNzycvYU/unsubscribe
.
To unsubscribe from this group and all its topics, send an email to
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-08-04 22:35:59 UTC
Permalink
On Tuesday 04 August 2015 23:30:46 'Edward Catmur' via ISO C++ Standard -
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
Previously a variant<bool, void> could have two values: true or false. Now
it can have three values: true, false or void{}. There's your extra state.
If the variant could never have assumed the void type as its state, then why
was void even allowed in the template parameter list in the first place?

Sounds like a design defect to me.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Edward Catmur' via ISO C++ Standard - Future Proposals
2015-08-04 22:46:39 UTC
Permalink
I'm not so sure - last I looked you could form tuple<int, void> despite the
resulting type being non-instantiable i.e. nullary. Such things have their
uses.
Post by Thiago Macieira
On Tuesday 04 August 2015 23:30:46 'Edward Catmur' via ISO C++ Standard -
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
Previously a variant<bool, void> could have two values: true or false.
Now
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
it can have three values: true, false or void{}. There's your extra
state.
If the variant could never have assumed the void type as its state, then why
was void even allowed in the template parameter list in the first place?
Sounds like a design defect to me.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to a topic in the
Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit
https://groups.google.com/a/isocpp.org/d/topic/std-proposals/05prNzycvYU/unsubscribe
.
To unsubscribe from this group and all its topics, send an email to
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-08-04 23:40:12 UTC
Permalink
On Tuesday 04 August 2015 23:46:39 'Edward Catmur' via ISO C++ Standard -
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
I'm not so sure - last I looked you could form tuple<int, void> despite the
resulting type being non-instantiable i.e. nullary. Such things have their
uses.
That one is fine, which is a case that would change interpretation with void
becoming instantiable.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 23:51:45 UTC
Permalink
Post by Thiago Macieira
On Tuesday 04 August 2015 23:46:39 'Edward Catmur' via ISO C++ Standard -
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
I'm not so sure - last I looked you could form tuple<int, void> despite
the
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
resulting type being non-instantiable i.e. nullary. Such things have
their
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
uses.
That one is fine, which is a case that would change interpretation with void
becoming instantiable.
This can hypothetically change interpretation in some vague sense, but what
code does this break? If people were using such a declaration as a type
list, then that doesn't at all break by this, for example. Even if they
were using void as a special tag type, that's not strictly a breaking
change, it could conceivably not be compatible with new possible uses of
void, but even would be really strange.

Why are we focused on this? If there is an actual problem people are
worried about in code, ground it with an example. This seems like a really
big stretch.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 22:53:57 UTC
Permalink
Post by Thiago Macieira
On Tuesday 04 August 2015 23:30:46 'Edward Catmur' via ISO C++ Standard -
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
Previously a variant<bool, void> could have two values: true or false.
Now
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
it can have three values: true, false or void{}. There's your extra
state.
If the variant could never have assumed the void type as its state, then why
was void even allowed in the template parameter list in the first place?
Sounds like a design defect to me.
+1 Unless there is some generic case that people have in mind and haven't
actually expressed, there is no explanation for why anyone would want this
special behavior for void.

Unless people have a clear, practical rationale for this other kind of
entity, I don't understand why it's even being discussed. What value would
it supposedly provide? All I see is more special casing, no aid in generic
programming, and no example of why you would want this in practice. If it's
just because people are clinging to a preconceived notion of "true"
emptiness, then I think we need to just accept that thinking of the void
data type in such a manner simply is not what would solve any of the
problems that we set out to solve. I'm not refuting that such a notion is
valid, only that there is no reason for us to have void model this
particular concept. Unless there is a practical example for *why* one would
want that particular representation of void, it's silly to even continue
down this path. In what cases would such a representation benefit our code?
It certainly doesn't simplify generic code in any way that the struct void
{}; does.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Zach Laine
2015-08-04 22:58:38 UTC
Permalink
On Tue, Aug 4, 2015 at 5:30 PM, 'Edward Catmur' via ISO C++ Standard -
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
Previously a variant<bool, void> could have two values: true or false. Now
it can have three values: true, false or void{}. There's your extra state.
I think previously a variant<bool, void> had 3 states: bool-true,
bool-false, and void. In other words, the discriminator could have been 0,
indicating that the bool type was active, or 1, indicating that the void
type was active. There were two states (true and false) within the
discriminator==0 case, and only one within the discriminator==1 case.
Right?

Zach
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Edward Catmur' via ISO C++ Standard - Future Proposals
2015-08-04 23:08:13 UTC
Permalink
If void is nullary (which it is at present, pretty much) then variant<bool,
void> could only have a discriminant of 0; the void type could not be
active because it is not possible to have a value of type void. Only if
void values are allowed does it become possible for the discriminant to
take a value of 1.
Post by Zach Laine
On Tue, Aug 4, 2015 at 5:30 PM, 'Edward Catmur' via ISO C++ Standard -
Post by 'Edward Catmur' via ISO C++ Standard - Future Proposals
Previously a variant<bool, void> could have two values: true or false.
Now it can have three values: true, false or void{}. There's your extra
state.
I think previously a variant<bool, void> had 3 states: bool-true,
bool-false, and void. In other words, the discriminator could have been 0,
indicating that the bool type was active, or 1, indicating that the void
type was active. There were two states (true and false) within the
discriminator==0 case, and only one within the discriminator==1 case.
Right?
Zach
--
---
You received this message because you are subscribed to a topic in the
Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit
https://groups.google.com/a/isocpp.org/d/topic/std-proposals/05prNzycvYU/unsubscribe
.
To unsubscribe from this group and all its topics, send an email to
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Paul Fultz II
2015-08-04 22:45:18 UTC
Permalink
Post by Zach Laine
Post by Thiago Macieira
Post by Zach Laine
Stop right there. You are using the value of the discriminator
"x.which()", which is an int with a runtime value. You never used the
values of empty1() and empty2().
There's no need to, because they can only assume one possible value.
It's like checking whether a value of type std::nullptr_t is null. It is, you
don't have to check.
That is precisely my point. This discussion started with Paul's statement
that use of the "struct void{}:" definition injects extra state into your
program that was not there before. I haven't yet seen it.
If `void` has zero values then `bool`, `variant<empty1, empty2>`,
`variant<empty1, empty2, void>` all have same number of values. However, if
we define `void` as `struct void {};` then `variant<empty1, empty2, void>`
has the same number of values as tribool. Essentially, adding an extra
state to the types. This is example is trivial and obvious, however, since
the assumptions of `void` is that it has no values, it could subtly easily
break other assumptions in the program.

Also, having a type represent zero values is really necessary for
completeness, especially if we add variant to the language since `void` is
the identity element for variant. Plus, the identity element would allow
monadic computations over variant types, which could help with defining
DSLs.
Post by Zach Laine
Zach
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 23:15:05 UTC
Permalink
Post by Paul Fultz II
Also, having a type represent zero values is really necessary for
completeness, especially if we add variant to the language since `void` is
the identity element for variant. Plus, the identity element would allow
monadic computations over variant types, which could help with defining
DSLs.
I have no doubt that you are trying to make a legitimate point here, but
please ground this with an example, and explain precisely why you feel it
makes sense to reuse "void" as such a type. Again, I don't refute that such
a type is a valid notion, only that it makes no sense for void to be that
type as making void such a type does not solve the actual problems we are
facing. The struct void {}; definition of void makes sensible generic code
work perfectly fine without special-casing void, and I have yet to see even
one practical example from you regarding how this definition of void would
provide any tangible benefit. Write a simple code example showing the
benefit and explains why "void" must be this type.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Paul Fultz II
2015-08-04 23:51:36 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Paul Fultz II
Also, having a type represent zero values is really necessary for
completeness, especially if we add variant to the language since `void` is
the identity element for variant. Plus, the identity element would allow
monadic computations over variant types, which could help with defining
DSLs.
I have no doubt that you are trying to make a legitimate point here, but
please ground this with an example, and explain precisely why you feel it
makes sense to reuse "void" as such a type. Again, I don't refute that such
a type is a valid notion, only that it makes no sense for void to be that
type as making void such a type does not solve the actual problems we are
facing.
It does make sense for `void` to be that type because it represents both
return type for a function that doesn't return anything and it represents
the subtype of all types(such as in `void*`).
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
The struct void {}; definition of void makes sensible generic code work
perfectly fine without special-casing void,
But there could be library solutions to help alleviate most of this,
something like this:

struct unit {};
template<class T>
using avoid = typename std::conditional<(std::is_void<T>()), unit, T>::type;


and I have yet to see even one practical example from you regarding how
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
this definition of void would provide any tangible benefit. Write a simple
code example showing the benefit and explains why "void" must be this type.
Its not just the benefits. Changing the fundamental type of void to have a
singular value could lead to subtle problems in assumptions made about
code(starting with injecting extra state). As for the benefits of a having
a type to represent zero state is a little beyond me, because I don't write
DSLs, but having tuples and variants form categories would allow them to
form an F-algebra(because I believe they are dual categories) which would
enable evaluation of recursive tree stuctures in a non-recursive fashion.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-05 00:28:12 UTC
Permalink
Post by Paul Fultz II
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Paul Fultz II
Also, having a type represent zero values is really necessary for
completeness, especially if we add variant to the language since `void` is
the identity element for variant. Plus, the identity element would allow
monadic computations over variant types, which could help with defining
DSLs.
I have no doubt that you are trying to make a legitimate point here, but
please ground this with an example, and explain precisely why you feel it
makes sense to reuse "void" as such a type. Again, I don't refute that such
a type is a valid notion, only that it makes no sense for void to be that
type as making void such a type does not solve the actual problems we are
facing.
It does make sense for `void` to be that type because it represents both
return type for a function that doesn't return anything and it represents
the subtype of all types(such as in `void*`).
You are missing what I am saying. I understand that you want void to mean
that because it coincides with your personal intuitions of "void", but it
has no actual benefit for specifically the c++ void type to be such a type.
If you want such a type we can make one, but using void for it does not
solve any of the problems that we are trying to solve that exist in actual
code. The reason void makes sense as struct void {}; is because that notion
coincides with how things like return values are used in generic code.
Don't decide what you personally thing void *should* mean a priori based on
intuition and have that direct you. Instead, figure out what definition
benefits the language based on how it is actually used in real code. I've
given a lot of actual examples and you still haven't produced any, but here
is another:

//////////
// Invoke a function, logging the arguments and return value
template <class Log, class Fun, class... P>
auto invoke_and_log(Log& log, Fun&& fun, P&&... args) {
auto my_log = log.template make_function_log<Fun(args...)>();
my_log.log_arguments(args...);
auto result = std::forward<Fun>(fun)(std::forward<P>(args)...);
my_log.log_success(result);
return result;
}
//////////

You can write this (untested, sorry if there are typos) and it will "work"
in c++. It, however, will currently fail if fun returns void. Why do you
feel that this is somehow beneficial? If we had an instantiable, regular
void, this would work even if the function returned void. You're still
logging a successful function invocation, and your personal serialization
function for void might be a no-op, but so what? What do you think you are
gaining by clinging to some preconceived notion of what void *ought* to be?
Let usage tell us the definition that benefits the writing of code.
Post by Paul Fultz II
But there could be library solutions to help alleviate most of this,
struct unit {};
template<class T>
using avoid = typename std::conditional<(std::is_void<T>()), unit, T>::type;
We already have to do these kinds of hacks. We are trying to eliminate the
need for them.
Post by Paul Fultz II
Its not just the benefits. Changing the fundamental type of void to have a
singular value could lead to subtle problems in assumptions made about
code(starting with injecting extra state).
Show. An. Example.
Post by Paul Fultz II
As for the benefits of a having a type to represent zero state is a little
beyond me, because I don't write DSLs, but having tuples and variants form
categories would allow them to form an F-algebra(because I believe they are
dual categories) which would enable evaluation of recursive tree stuctures
in a non-recursive fashion.
I do write EDSLs and I deal with variants all of the time, and I don't see
the benefit. Maybe there is a theoretical benefit to having such a type and
I just haven't encountered it, which is why I'd like an example, but it
should also clarify why we need "void" specifically to be that type. Having
void be equivalent to a regular struct with no members solves real problems
when writing generic code, and I've shown many actual examples of that.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Matthew Woehlke
2015-08-05 13:54:02 UTC
Permalink
On 2015-08-04 19:15, 'Matt Calabrese' via ISO C++ Standard - Future
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Paul Fultz II
Also, having a type represent zero values is really necessary for
completeness, especially if we add variant to the language since `void` is
the identity element for variant.
I have no doubt that you are trying to make a legitimate point here, but
please ground this with an example, and explain precisely why you feel it
makes sense to reuse "void" as such a type.
...because that's how it is *already* used?

I've been rather half following the discussion, albeit with some
concern. I'm certainly not against making it easier to use 'void' in
generic programming, however I think it's critical to keep in mind that
the first requirement be that the following program does not change in
meaning:

void foo();

int main()
{
foo();
return 0;
}

void foo(void)
{
return;
}

(Note in particular that making void a regular type - i.e. 'struct
void{}' - WILL BREAK THIS! NOT ACCEPTABLE!)

Rather (without getting into some of the more esoteric template
questions), it seems like the following should be made legal:

// allow void declaration; initialization is irrelevant
void x;

// allow void assignment; exactly equivalent to:
// (void)foo(); void y;
void y = foo();

// allow deduction from void
auto z = x;

// allow passing void declaration as argument to function
// taking zero arguments
foo(x);

// allow comparing void declarations (see also relational operators
// for Python's 'None')
assert(x == y);
assert(!(x != y));

// (optional) "allow" taking the address of a void declaration
auto p = &x;
assert(p == nullptr); // ...but 'x' has no storage

I would consider any proposal that doesn't provide at least the above a
waste of time, if not a step in the wrong direction. In particular, I'm
concerned that there doesn't seem to be enough discussion how to
preserve calling a function with argument list "(void)" with no
arguments, or calling a function taking no arguments with a single
argument of type 'void'. These are required to add benefit without
breaking existing code, and they necessitate continuing to handle 'void'
specially.

'void' is the zero-value type and SHOULD be handled specially! The
objective here is to also allow it to be used in generic programming
with less specialization.

On 2015-08-04 20:28, 'Matt Calabrese' via ISO C++ Standard - Future
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
// Invoke a function, logging the arguments and return value
template <class Log, class Fun, class... P>
auto invoke_and_log(Log& log, Fun&& fun, P&&... args) {
auto my_log = log.template make_function_log<Fun(args...)>();
my_log.log_arguments(args...);
auto result = std::forward<Fun>(fun)(std::forward<P>(args)...);
my_log.log_success(result);
return result;
}
This will work, with the above, if 'Log::log_success(void)' exists. It's
not necessary for this example to make 'void' a regular type.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
If we had an instantiable, regular void, this would work even if the
function returned void.
"Instantiable" may or may not be correct terminology here. In the 'void'
case, 'result' names an entity, but no storage is ever associated with
it; basically, 'void x' is just telling the compiler to allow you to
write 'x' again somewhere a value is not actually needed, e.g. 'foo(x)'
for 'void foo()' or 'return x' in a void function.
--
Matthew
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-05 18:31:00 UTC
Permalink
Post by Matthew Woehlke
On 2015-08-04 19:15, 'Matt Calabrese' via ISO C++ Standard - Future
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Paul Fultz II
Also, having a type represent zero values is really necessary for
completeness, especially if we add variant to the language since `void`
is
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Paul Fultz II
the identity element for variant.
I have no doubt that you are trying to make a legitimate point here, but
please ground this with an example, and explain precisely why you feel it
makes sense to reuse "void" as such a type.
...because that's how it is *already* used?
No, it's really not. Even in the language as it is today it is
inconsistently used as a unit type. It's just incomplete. As someone
already mentioned, a true lack of result would be more accurately
represented by a function not returning at all (I.E. a [[noreturn]]
function that terminates). That is not what a void return type means in C
nor in C++. Similarly, in C++, it is already legal to return a void
expression in the return statement of a function that returns void. Here we
appear to be treating it as a value and copying it around, as that's what
such returning means with all other types. These certainly point to void
simply being a partially specified unit type rather than something else.
Not that any of this matters, because we are not trying to put a label on
what void in C++ currently is, but rather, we are trying to figure out what
definition of void would most benefit the language in the future. Instead
of niggling over possible interpretations of the words "void" or
"emptiness," we should be looking at what definition of void with respect
to C++ actually aids in developing software. We have actual usage and
tangible examples for why it is extremely useful for void to simply be a
regular type, regardless of whatever label you want to give it. I have seen
NO tangible examples from the opposing crowd for why it should be something
less.

As an aside, in C, when void was specified, a full definition of void
didn't really matter much because the use-cases for void being regular come
about when writing more abstract, generic code, which is less common in C
outside of sophisticated macros. It doesn't really matter much in C because
the uses for a complete void type don't usually come up in practice.
Because of this, I'd personally argue that the nature of void as an
incomplete type seems much more incidental due to there not having been
obvious use-cases for it at the time (we have use-cases now). Even if the
choice was NOT incidental, there are very strong reasons that show that it
is much more beneficial to treat it as a regular type, so regardless of how
you want to interpret its current meaning or reason for being, there are
very clear benefits to making it complete and regular. You are only hurting
progress and people writing actual code if you ignore that.
Post by Matthew Woehlke
void foo();
int main()
{
foo();
return 0;
}
void foo(void)
{
return;
}
(Note in particular that making void a regular type - i.e. 'struct
void{}' - WILL BREAK THIS! NOT ACCEPTABLE!)
No, you just specify that "return;" is shorthand for "return void();". No
one is suggesting that we break code like this. I don't think anyone in
this thread was under that assumption. This isn't really much of a change,
since you don't have to write "return;", but rather, you can already, in
valid C++, write "return g();", where "g" is another function that returns
void. It's more just syntactic sugar. Of note, if you really wanted to, you
could even generalize that notion to mean that "return;" is just syntactic
sugar for "return T();" where T is the return value of the function (I'm
not suggesting that we do this in the language, I'm just using it as an
example of its interpretation as syntactic sugar).
Post by Matthew Woehlke
Rather (without getting into some of the more esoteric template
// allow void declaration; initialization is irrelevant
void x;
// (void)foo(); void y;
void y = foo();
// allow deduction from void
auto z = x;
// allow passing void declaration as argument to function
// taking zero arguments
foo(x);
// allow comparing void declarations (see also relational operators
// for Python's 'None')
assert(x == y);
assert(!(x != y));
// (optional) "allow" taking the address of a void declaration
auto p = &x;
assert(p == nullptr); // ...but 'x' has no storage
This is just silly. What are you gaining by arbitrarily limiting to these
operations like this and defining them in this manner? Please refer to the
many actual examples that I've accumulated throughout this thread
(including why you shouldn't have argument collapsing) and the 0 examples
that you have provided regarding the benefits of your representation for
void in C++. Unless you show why in practice what you are proposing
improves over void simply being a regular type, and actually succeeds at
removing the special casing of void in generic programming, then the
suggested behavior is providing no visible worth. So far you have done
neither. Don't dictate what void "should" be. Deduce what void should be
based on what makes it easy to write meaningful and powerful code.
Post by Matthew Woehlke
In particular, I'm
concerned that there doesn't seem to be enough discussion how to
preserve calling a function with argument list "(void)" with no
arguments, or calling a function taking no arguments with a single
argument of type 'void'.
??? What is "enough." This was already talked about pretty extensively. It
is not proposed that void foo(void) {} changes meaning, because that would
be a huge breaking change. There have been at least 3 suggestions presented
in 3 different replies for how to actually represent a unary function
taking void.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-05 19:03:39 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Andrey Semashev
void foo();
int main()
{
foo();
return 0;
}
void foo(void)
{
return;
}
(Note in particular that making void a regular type - i.e. 'struct
void{}' - WILL BREAK THIS! NOT ACCEPTABLE!)
No, you just specify that "return;" is shorthand for "return void();". No
one is suggesting that we break code like this. I don't think anyone in
this thread was under that assumption. This isn't really much of a change,
since you don't have to write "return;", but rather, you can already, in
valid C++, write "return g();", where "g" is another function that returns
void. It's more just syntactic sugar. Of note, if you really wanted to, you
could even generalize that notion to mean that "return;" is just syntactic
sugar for "return T();" where T is the return value of the function (I'm
not suggesting that we do this in the language, I'm just using it as an
example of its interpretation as syntactic sugar).
I just looked back and noticed you mean the void foo(); and void(void) {}
difference. I thought you meant the return statement. I went over this
later on in this reply anyway, but as mentioned several times in previous
replies in this very thread, we are not proposing breaking the code that
you've shown. No meaning would change here because the implications would
be too great. Instead, if you are in a place where void is not a dependent
type, it has the current meaning. If dependent, which is where we actually
care about this and where void would not produce a valid function
declaration in C++, it means unary void. If you want to represent a unary
void function in a non-dependent context you'd just use some other syntax,
which is unfortunate, but fine, and doesn't hurt generic code. There have
been multiple suggestions for what that syntax would be.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Patrice Roy
2015-08-04 19:38:16 UTC
Permalink
I think it's reasonable to claim that since we can do such things as

bool is_empty1(empty1) { return true; }
bool is_empty1(...) { return false; }

auto x = empty1{};
if (is_empty1(x))
{
// ...
}

we can hold that a monostate type such as empty1 has a value.

I'm not sure where to go to with changes to the semantics of void, as it
has far-ranging implications, but monostates and values seem reasonable to
me.
Post by Zach Laine
Post by Paul Fultz II
Post by Zach Laine
Post by Paul Fultz II
Post by Bengt Gustafsson
How about just defining that void behaves like an empty struct? This
avoids us having to discuss every particular case. There are still the
potentials for code breakage with tricky SFINAE but I don't see how that
can ever be avoided if we change the behaviour of void at all. Note: I
don't mean that void should BE an empty struct in that you can inherit from
it, but that it acts like one when used in code.
But having void act like an empty struct, then it becomes a type with
one value. However, `void` should mean a type with zero values(like a
function that doesn't return anything). Of course, changing `void` from
having zero values to one value could break a lot of assumptions made about
code. I think its a bad idea. For some generic code, it may be safe to
assume void as having one value instead of zero, but it would be better to
use a library solution for this instead of baking in potential unsafe type
assumptions into the language.
Again, I don't see the one value you mention. Please point to the value
struct foo
{
};
Where is it? Objects of type "foo" have an address, but no value,
right? What am I missing?
auto x = foo();
So what value does `x` have? It has to have a value, and it is `foo()`.
foo f()
{ return foo(); }
If `foo` didn't have a value, then we wouldn't be able to return it from
a function. Also, you can observe that the values start to add up when we
start to sum them using a variant. How many values does `foo` have now?
struct empty1 {};
struct empty2 {};
typedef variant<empty1, empty2> foo;
It has two: `empty1()` and `empty2()`.
Please write an if statement predicated on the values of empty1() and
empty2() that takes different paths depending on runtime state, without
relying on UB.
My point is that there is no non-UB-invoking observable runtime state for
these objects. They have no values that are meaningful to my program's
state. They are meaningful at compile time only.
Zach
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Miro Knejp
2015-08-04 17:34:19 UTC
Permalink
How about just defining that void behaves like an empty struct? This avoids us having to discuss every particular case. There are still the potentials for code breakage with tricky SFINAE but I don't see how that can ever be avoided if we change the behaviour of void at all. Note: I don't mean that void should BE an empty struct in that you can inherit from it, but that it acts like one when used in code.
But having void act like an empty struct, then it becomes a type with one value. However, `void` should mean a type with zero values(like a function that doesn't return anything). Of course, changing `void` from having zero values to one value could break a lot of assumptions made about code. I think its a bad idea. For some generic code, it may be safe to assume void as having one value instead of zero, but it would be better to use a library solution for this instead of baking in potential unsafe type assumptions into the language.
struct foo
{
};
Where is it? Objects of type "foo" have an address, but no value, right? What am I missing?
Which is the very definition of a unit type. Technically they are indistinguishable. What changes that in C++ is the fact that we can assign every object a memory address and therefore give every instance an *identity*. One cannot write a meaningful operator== that can distinguish two foo operands without taking their address, but at that point we’re comparing identities, not states/values.

@Zach:
Regarding void: https://en.wikipedia.org/wiki/Unit_type#Void_type_as_unit_type <https://en.wikipedia.org/wiki/Unit_type#Void_type_as_unit_type>
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Roland Bock
2015-08-05 07:42:24 UTC
Permalink
Post by Miro Knejp
On Tuesday, August 4, 2015 at 4:00:41 AM UTC-5, Bengt
How about just defining that void behaves like an empty
struct? This avoids us having to discuss every particular
case. There are still the potentials for code breakage
with tricky SFINAE but I don't see how that can ever be
I don't mean that void should BE an empty struct in that
you can inherit from it, but that it acts like one when
used in code.
But having void act like an empty struct, then it becomes a
type with one value. However, `void` should mean a type with
zero values(like a function that doesn't return anything). Of
course, changing `void` from having zero values to one value
could break a lot of assumptions made about code. I think its
a bad idea. For some generic code, it may be safe to assume
void as having one value instead of zero, but it would be
better to use a library solution for this instead of baking
in potential unsafe type assumptions into the language.
struct foo
{
};
Where is it? Objects of type "foo" have an address, but no
value, right? What am I missing?
Which is the very definition of a unit type. Technically they are
indistinguishable. What changes that in C++ is the fact that we can
assign every object a memory address and therefore give every instance
an *identity*. One cannot write a meaningful operator== that can
distinguish two foo operands without taking their address, but at that
point we’re comparing identities, not states/values.
Regarding
void: https://en.wikipedia.org/wiki/Unit_type#Void_type_as_unit_type
Ok, so that page says:

In C, C++, C#, and Java, void expresses the empty type. The unit type in
C would be struct {}, but an empty struct is forbidden by the C language
specification .


But if we go all Wikipedia, then also add this page here:

https://en.wikipedia.org/wiki/Bottom_type (aka empty type)
In type theory, a theory within mathematical logic, the bottom type is
the type that has no values. It is also called the zero or empty type.
The bottom type is sometimes confused with the /so-called "void type",
which is actually a unit type/, albeit one with no defined operations.

So Wikipedia says:

* void is the bottom type (no value)
* void is a unit type (exactly one value)

Wikipedia contradicts itself, thus it cannot really be used to decide. ;-)

I guess that is where most of the confusion in this thread arises from.
Some want to interpret void as the value-less bottom type, others want
to interpret void as a unit type (exactly one value).

Thus, when making statement about how void should behave, you should
state whether you are in the "bottom type" or in the "unit type" fraction.


As of now, I am a member of the "unit type" fraction: void does have a
value which - for instance - says that a function has finished
regularly, in contrast to not returning at all, throwing an exception,
or aborting the program.


Cheers,

Roland
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Miro Knejp
2015-08-05 09:58:25 UTC
Permalink
In C, C++, C#, and Java, void expresses the empty type. The unit type in C would be struct {}, but an empty struct is forbidden by the C language specification .
https://en.wikipedia.org/wiki/Bottom_type <https://en.wikipedia.org/wiki/Bottom_type> (aka empty type)
In type theory, a theory within mathematical logic, the bottom type is the type that has no values. It is also called the zero or empty type. The bottom type is sometimes confused with the so-called "void type", which is actually a unit type, albeit one with no defined operations.
* void is the bottom type (no value)
* void is a unit type (exactly one value)
Wikipedia contradicts itself, thus it cannot really be used to decide. ;-)
I guess that is where most of the confusion in this thread arises from. Some want to interpret void as the value-less bottom type, others want to interpret void as a unit type (exactly one value).
Thus, when making statement about how void should behave, you should state whether you are in the "bottom type" or in the "unit type" fraction.
As of now, I am a member of the "unit type" fraction: void does have a value which - for instance - says that a function has finished regularly, in contrast to not returning at all, throwing an exception, or aborting the program.
Yay for contradictions. I’d say that void as it stands in C++ now is somewhere between the bottom and unit type. It is really neither of them:

- Functions marked with void do return to the caller but shouldn’t if void was the bottom type
- Functions marked with void or taking void do not take or return values, but they would take or return the singular unit value if void were a unit type

In a perfect world C++ would have a builtin unit type and a builtin bottom type (so everyone would use those by default instead of rolling their own), right now it really has neither. Considering the status quo void would be much more useful in practice as a unit type. The vast majority of functions do terminate so the need for a real bottom type is minuscule.

I don’t think you’d have to change the ABI to support void as a unit type. The compiler can simply pretend that it’s passing a unit value to nullary external functions and it can make a new unit value out of thin air for functions returning void. Right now you cannot have void in the middle of the argument list so there is no possibility in ABI breakage for existing functions. Once the void unit type is properly mangled new functions can have it anywhere in the argument list.

The problem I still see is that decltype(foo(T())) can already be used to filter out void in SFINAE so if it were to suddenly be well-formed for T=void that can change the behavior of existing code. Where is that damn ship sailing to anyways, someone make it come back.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
j***@dropbox.com
2015-08-05 17:24:08 UTC
Permalink
Post by Miro Knejp
Yay for contradictions. I’d say that void as it stands in C++ now is
- Functions marked with void do return to the caller but shouldn’t if void
was the bottom type
- Functions marked with void or taking void do not take or return values,
but they would take or return the singular unit value if void were a unit
type
The behavior of pointer-to-void makes void seem a lot like bottom, but I
think in general it's much closer to unit. "Does not return a value" is
tricky phrasing, since in the type theory where 'bottom' and 'unit' come
from, "does not return a value" means "does not return at all". And there
are already some contexts in which void seems very unit-like:

void f() { return void(); } // this is already legal
void g() { return f(); } // so is this

As for parameters, I think that debate belongs with the tuple /
multiple-return-value discussion. (You could even argue that 'f()' looks a
lot like passing a 0-tuple to f, but that's stretching the analogy pretty
far...)
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Matthew Woehlke
2015-08-05 17:53:31 UTC
Permalink
You could even argue that 'f()' looks a lot like passing a 0-tuple to
f, but that's stretching the analogy pretty far...
You mean like this?

$ python
... print len(args)
...
foo()
0

;-)

Or this?

$ cat arglist.cpp
#include <cstdio>

template <typename... Args>
void foo(Args const&... args)
{
printf("%s called with %d arguments\n",
__func__, sizeof...(Args));
}

int main()
{
foo();
return 0;
}

$ g++ -std=c++11 arglist.cpp && ./a.out
foo called with 0 arguments
--
Matthew
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2015-08-04 09:06:46 UTC
Permalink
Post by Arthur O'Dwyer
I understand that you want to be able to construct such an object via std::make_tuple(1,
void()). I consider *that* part controversial.
Yes, you say it's "controversial" but you don't ever explain why you think
it doesn't make sense. It makes perfect sense and I've even given
real-world examples in this thread that directly involve make_tuple and
void. I may be the most active person in this thread right now, but I am
not at all unique in this -- the notion that void should be like any other
type is understood by many already in the generic programming world. All
you have stated is simply that you intuitively feel a certain function type
is "nonsensical" because it contains void in it. I'm sorry, but unless you
have an explanation for why you think that's the case, then I'm simply not
going to give your personal intuition any credit at all. Actually address
*what* about it you think is nonsensical if you want to have a serious
discussion about this, and in doing so, explain how your view would affect
the examples already presented in this thread in a positive way.

I don't know how to reconcile std::tuple<void> with make_tuple. I don't
Post by Arthur O'Dwyer
think that introducing a new "unary void" is the right way to do it.
Instead of introducing "unary void", I would rather leave std::tuple<void>
ill-formed, or even say that it is well-formed but that instances of it
cannot be constructed via any kind of make_tuple.
That's the kind of special casing we are removing and there is nothing
illogical with a single void parameter. What do you think you are
accomplishing by having it illegal? How many examples of it being used in
practice do I have to give (I've already given several)? I apologize that
I'm getting really annoyed here and it's starting to show, but PLEASE
actually back up your arguments or I'm going to eventually just stop taking
your replies seriously.

In an attempt to actually make progress here, let's back up. What of the
following do you disagree with and why? Please do take the "why" part
seriously.

we are trying to remove the special casing that is necessary in generic
code when a dependent type may be void.
void can be thought of as a complete, empty, regular type (a formulation of
the change that we are proposing, critique this if you want).
tuple<int, int> is a sensible type that has an int member and another int
member.
tuple<void, int> is a sensible type that has a void member and an int
member (you've already agreed to this).
the construction of a tuple should be syntactically consistent whether it
is a tuple<int, float> or tuple<double, char> such that it can easily be
constructed when the types are dependent.
the construction of a tuple should be syntactically consistent whether it
is a tuple<int, float> or tuple<int, void> such that it can easily be
constructed when the types are dependent.
having make_tuple work with void arguments eliminates special-casing in
generic code.
have make_tuple fail to work when one of the types is void forces users to
special-case their code when an operand would be void.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2015-08-03 06:09:45 UTC
Permalink
Post by Arthur O'Dwyer
I think it's extremely debatable whether
std::tuple<int, void, int>
or
std::array<void, 3>
ought to be permitted by the language.
I’m not following very closely, but note that std::tuple does have std::ignore which acts as a bit-bucket.

Perhaps it could be sufficient to map void to a bit-bucket type, as an alternative approach to regularize. Either way, you still have to deal with functions taking zero vs. one parameter.
Post by Arthur O'Dwyer
template<class T> struct simple_future {
regularize<T> rt;
void set(T p) { rt.set(p); }
};
void set( std::tuple_element< std::tuple< T >, unpack_sequence( ! std::is_void_v< T > ) > const & ... p )
{ rt.set( p ... ); }

The pack p is length zero or one, depending on the Boolean result of is_void. The unpack_sequence operator is from a recent thread here; it creates an unexpanded pack of std::size_t values. It’s OK to mention type typename std::tuple< void > as long as it doesn’t get instantiated.

Ugly? Yes. Too clever? Maybe. Just mentioning it.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Loading...