What is a void() prvalue and how is it used?

91 Views Asked by At

Reading cppreference's description of functional style cast, I stumbled upon the following explanation for void():

If target-type is (possibly cv-qualified) void, the expression is a void prvalue without a result object (since C++17)

Now I understand that I can create a void() prvalue like so, but what can I use it for and why is this even possible?

2

There are 2 best solutions below

0
463035818_is_not_an_ai On

I suppose you refer to this paragraph

  1. The functional-style cast expression consists of a simple type specifier or a typedef specifier (in other words, a single-word type name, that is, cases such as unsigned int(expression) and int*(expression) are not valid), followed by a comma-separated list of expressions in parentheses.

and the bullet

  • If there's no expression in parentheses: if target-type names a non-array complete object type, this expression is a prvalue of type target-type, designating a temporary (until C++17)whose result object is (possibly with added cv-qualifiers) (since C++17) of that type. If target-type is an object type, the object is value-initialized. If target-type is (possibly cv-qualified) void, the expression is a void prvalue without a result object (since C++17).

If you put the standardese language aside, its actually not that complicated. I suppose you wonder why one would write something like this:

void bar() {
    return void();
}

According to the above this is valid, but not really useful. Now consider you have a function template like this:

template <typename T>
auto foo() {
    return typename T::value_type();
}

Then you can instantiate it with a T whose value_type is void:

struct X{
    using value_type = void;
};
int main() {
    foo<X>();
}

There is no object returned, but the type of the expression T::value_type in foo<X> is void, the function has void return type.

0
Jarod42 On

but what can I use it for

There are several places where it is useful and directly used:

  • SFINAE with decltype (superseed by requires in C++20)

    template <typename T>
    auto print_foo(const T& value)
    -> decltype(foo(t), void()) // SFINAE on foo(t), but return void
    {
        std::cout << foo(t);
    }
    
  • split expression to handle evil operator, (if one side is void, there are no custom available operator, usable).

    template <Typename F, typename...Ts>
    void for_each_call(F f, const Ts&... args)
    {
        ((f(args), void()), ...);
    }
    

There is also after substitution in generic code:

template <typename T>
T foo()
{
    return T();
}

foo<void>() is valid.