Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When return statement, comma operator, braced-init-list and std::unique_ptr come together

Tags:

c++

In the code below, why do I get compilation error for n = 8 whereas all other cases are OK? My intent was to report some error and return empty pointer without cluttering the code with unnecessary braces. I will use nullptr for this purpose but I'm curious why {} fails to compile with comma operator whereas it works alone.

You can play with this code here. I used C++20 setting.

#include <cstdint>
#include <memory>

void oops(const char*) {
}

std::unique_ptr<uint8_t[]> please_do(int n) {
    if (n == 1)
        return std::unique_ptr<uint8_t[]>(); // compiles ok
    if (n == 2)
        return oops(""), std::unique_ptr<uint8_t[]>(); // compiles ok

    if (n == 3)
        return std::make_unique<uint8_t[]>(n); // compiles ok
    if (n == 4)
        return oops(""), std::make_unique<uint8_t[]>(n); // compiles ok

    if (n == 5)
        return nullptr; // compiles ok
    if (n == 6)
        return oops(""), nullptr; // compiles ok

    if (n == 7)
        return {}; // compiles ok
    if (n == 8)
        return oops(""), {}; // why compilation error?

    return nullptr; // compiles ok
}

int main() {
    please_do(42);
    return 0;
}

GCC 9.2 output:

<source>: In function 'std::unique_ptr<unsigned char []> please_do(int)':

<source>:26:26: error: expected primary-expression before '{' token

   26 |         return oops(""), {}; // why compilation error?

      |                          ^

<source>:26:25: error: expected ';' before '{' token

   26 |         return oops(""), {}; // why compilation error?

      |                         ^~

      |                         ;

Clang 9.0.0 output:

<source>:26:16: error: no viable conversion from returned value of type 'void' to function return type 'std::unique_ptr<uint8_t []>'

        return oops(""), {}; // why compilation error?

               ^~~~~~~~

/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/unique_ptr.h:528:7: note: candidate constructor not viable: cannot convert argument of incomplete type 'void' to 'std::unique_ptr<unsigned char [], std::default_delete<unsigned char []> > &&' for 1st argument

      unique_ptr(unique_ptr&& __u) noexcept

      ^

/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/unique_ptr.h:533:12: note: candidate constructor template not viable: cannot convert argument of incomplete type 'void' to 'std::nullptr_t' (aka 'nullptr_t') for 1st argument

        constexpr unique_ptr(nullptr_t) noexcept

                  ^

/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/unique_ptr.h:681:7: note: candidate constructor not viable: cannot convert argument of incomplete type 'void' to 'const std::unique_ptr<unsigned char [], std::default_delete<unsigned char []> > &' for 1st argument

      unique_ptr(const unique_ptr&) = delete;

      ^

/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/unique_ptr.h:542:2: note: candidate template ignored: could not match 'unique_ptr<type-parameter-0-0, type-parameter-0-1>' against 'void'

        unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept

        ^

<source>:26:24: error: expected expression

        return oops(""), {}; // why compilation error?

                       ^
like image 758
4LegsDrivenCat Avatar asked Oct 20 '25 04:10

4LegsDrivenCat


1 Answers

{} is not an expression. return {...}; is its own special syntax (a form of list initialization) that figures out the return type from the function's signature, as if by Ret{...} where Ret is the return type:

Ret f() {
    return {...}; // special syntax for return; {args...} need not be a valid expression
}
// same as
Ret f() {
    return Ret{...}; // generic return <expression> syntax; Ret{args...} must be a valid expression
}

There is no special syntax in return a, b. It's just the expression a, b being returned using the normal return syntax. The comma operator requires both a and b to be expressions, but since {} is not an expression, expressions like oops(""), {} is invalid syntax.

As an aside, there are other places where {...} can be used that make it look like it's an expression when it's actually not, e.g. function calls:

void f(std::string);
f({5, 'a'})

Again, though this looks like {5, 'a'} is an expression with type std::string (as is the intention), it is not. The {5, 'a'} is part of the function call itself, and its type is decided by overload resolution.

As to the compiler errors, both of them are being confused by the fact that oops(""), {} is invalid syntax as an expression. GCC appears to have read oops(""),, and is thus expecting an expression to follow. But expressions cannot start with {, so it reads that character and immediately barfs, complaining that it was expecting an expression to begin. Clang does the same thing. I think that Clang then continues, in its quest to be "helpful", to pretend that you just didn't write the , {}; part (that is, as if you wrote return oops("");), and issues an error for that, too. This behavior is e.g. what allows Clang to provide useful type errors even if you misspell something; that is, if you were to run Clang on

void supercalifragilisticexpialidocious(std::string);
int main() { supercalifragilisticexpialidociuos(7); }

it would first issue an error for the bad spelling (-ous -> -uos), suggest the correct spelling, and then it would issue an error for the type mismatch between the int 7 and the argument type std::string as if you had already fixed the spelling. Similarly, here, it's issuing one error for the bad syntax and then a second error as if you had gotten rid of the bad syntax. However, it's not very helpful in this case since the "fix" it imagines you using is not the actual fix.

like image 189
HTNW Avatar answered Oct 21 '25 18:10

HTNW



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!