c++castinglanguage-lawyerautoc++23

auto ( expression ) in direct initialization argument


In C++23 we have explicit type convertion auto ( expression ), and according to cppreference it is already supported by both GCC and Clang.

I have a question where such cast does not occur for some reason. Consider the program:

// OK, x = 0
int x(int(auto(0)));

// OK, f is function template
int f(auto(x));

// error
int y(int(auto(x)));

Here x declaration is accepted, but very similar y declaration does not. Online demo: https://godbolt.org/z/f9zor3eTv

GCC:

error: 'auto' parameter not permitted in this context

Clang:

error: 'auto' not allowed in function prototype that is not a function declaration

Is declaration of y really illegal and compilers are correct in rejecting it?


Solution

  • Had you had a type instead of auto, it would read like:

    int y(int(long(x)));
    // ->
    int y(int(long x));
    // ->
    int y(int(*)(long));
    

    However, you cannot put auto in place of long.

    [dcl.fct]p22:

    An abbreviated function template is a function declaration that has one or more generic parameter type placeholders ([dcl.spec.auto]). [...]

    [dcl.spec.auto.general]p2

    A placeholder-type-specifier of the form type-constraintopt auto can be used as a decl-specifier of the decl-specifier-seq of a parameter-declaration of a function declaration or lambda-expression and, if it is not the auto type-specifier introducing a trailing-return-type (see below), is a generic parameter type placeholder of the function declaration or lambda-expression.

    ... So the auto can only be a decl-specifier directly in the decl-specifier-seq of the type, not nested further. Basically, if you can move the auto all the way to the left, it will work.

    So these abbreviated function templates work:

    int y(auto x);
    int y(auto* x);
    int y(auto const*& x);
    int y(const auto*& x);
    int y(auto* x[][12]);
    int y(auto (*x)[3][4]);
    int y(auto (*fn)(int x));
    // Last one can be written as:
    int y(auto(int x));
    int y(auto(int(x)));
    

    And these ones won't:

    int y(std::vector<auto> x);
    int y(void (*fn)(auto));
    int y(int x[sizeof(auto)]);
    

    The exact same restrictions apply to placeholder types in initializers, like auto x = ...;:

    int f(int);
    
    auto (*ptr1)(int) = f;  // Works
    // int (*ptr2)(auto) = f;  // Doesn't