Ответ 1
Возврат по значению не требует определения типа. Достаточное объявление достаточно
Объявление функции, возвращающей значение, не требует определения типа. Хорошо сформированная демо:
struct S;
S foo();
struct S {};
int main() {
foo();
}
S foo() {}
Определение или вызов функции, которая возвращает значение, требует определения типа. Стандартный черновик [basic.def.odr]:
5 Точно одно определение класса требуется в блоке трансляции, если класс используется таким образом, чтобы тип класса был полным. [Пример:... [snip]... [Примечание. Правила деклараций и выражений описывают, в каких контекстах требуются полные типы классов. Тип класса T должен быть полным, если:
- [надрез]
- 5.9 определена функция с типом возвращаемого типа или типа аргумента типа T ([basic.def]) или вызвана ([expr.call]) или
- [надрез]
Объявление функции с неполным возвращаемым типом неявно разрешено в силу запрета любым из правил в списке.
Правило переформулируется позже в стандарте, и оно смягчается исключением [dcl.fct] (благодаря @cpplearner для указания этого правила):
11 Типы не должны определяться в обратном порядке или типах параметров. Тип параметра или тип возвращаемого значения для определения функции не должен быть неполным (возможно, cv-квалифицированным) типом класса в контексте определения функции, если функция не удалена ([dcl.fct.def.delete]).
Неплохое демо:
struct S;
S foo(){} // oops
struct S {};
Другая неудачная демонстрация:
struct S;
S foo();
int main() {
foo(); // oops
}
struct S {};
S foo() {}