[dcl.fct.def.general]/2:
The type of a parameter or the return type for a function definition shall not be an incomplete or abstract (possibly cv-qualified) class type in the context of the function definition unless the function is deleted ([dcl.fct.def.delete]).
But [dcl.spec.auto]/10:
Return type deduction for a function template with a placeholder in its declared type occurs when the definition is instantiated even if the function body contains a return
statement with a non-type-dependent operand.
So with B
, it's ill-formed by the first rule. But with auto
, deduction doesn't take place until the function is instantiated... by which point the type is complete, so it's fine.
Note that the first rule only applies to the definition, which is why do_f()
is okay. You can have declarations which return incomplete types.
The above wording technically doesn't apply to this case. We don't have a function template. But the intent is for it to apply to any kind of templated thing. There's a PR to editorially fix this from:
Return type deduction for a function template with a placeholder [...]
To:
Return type deduction for a templated entity that is a function or function template with a placeholder in its
Which does apply here.