Search code examples
c++initializationlanguage-lawyerfunction-pointersauto

Is it possible to use the `auto` keyword as a return type in a function pointer declaration with initialization?


The following code compiles successfully both with clang++ 3.8.0 and g++ 7.2.0 (the compilation flags are -std=c++14 -O0 -Wall -Wextra -Werror -pedantic-errors):

#include <iostream>


int foo_int(int)
{
    std::cout << "int foo(int)" << std::endl;

    return 0;
}

void foo_void(int)
{
    std::cout << "void foo(int)" << std::endl;
}

auto foo_auto_int(int)
{
    std::cout << "auto foo(int), auto == int" << std::endl;

    return 0;
}

auto foo_auto_void(int)
{
    std::cout << "auto foo(int), auto == void" << std::endl;

    return void();
}


int main()
{
    auto (*fi)(int) = foo_int;
    auto (*fv)(int) = foo_void;
    auto (*fai)(int) = foo_auto_int;
    auto (*fav)(int) = foo_auto_void;

    (void)fi(0);
    fv(0);
    (void)fai(0);
    fav(0);
}

Is it a valid C++ code?

Note that the decltype(auto) is rejected both by clang++ and g++ in the same situation.


Solution

  • The compilers are behaving correctly.

    From [dcl.spec.auto]

    The auto and decltype(auto) type-specifiers are used to designate a placeholder type that will be replaced later by deduction from an initializer.

    [...]

    auto or decltype(auto) shall appear as one of the decl-specifiers in the decl-specifier-seq and the decl-specifier-seq shall be followed by one or more declarators, each of which shall be followed by a non-empty initializer.

    Which is saying auto and decltype(auto) can only be written with the specifiers you write in the front of a declaration (static, virtual etc), with their types deduced from the immediately following declarators with initializers.

    The case of auto

    The declarator in the case of auto (*fi)(int) = foo_int; is (*fi)(int) which is of the form

    ( ptr-operator declarator-id ) ( parameter-declaration-clause )
    

    Hence auto (*fi)(int) = foo_int; is valid, provided the deduction succeeds, which it does. Likewise for the other few.

    The case of decltype(auto)

    From [dcl.spec.auto.deduct], given a type T containing a placeholder type

    If the placeholder is the decltype(auto) type-specifier, T shall be the placeholder alone.

    Which means adding anything else is illegal

    int i;
    decltype(auto)* p = &i;  // error, declared type is not plain decltype(auto)
    

    Hence decltype(auto) (*fi)(int) = foo_int; is illegal.