Estou escrevendo isso como uma resposta separada, e não apenas um comentário, porque discordo da resposta de Luc Touraille, não com base na legalidade, mas em software robusto e no perigo de erros de interpretação.
Especificamente, tenho um problema com o contrato implícito do que você espera que os usuários da sua interface tenham que saber.
Se você estiver retornando ou aceitando tipos de referência, estará apenas dizendo que eles podem passar por um ponteiro ou referência que, por sua vez, eles podem ter conhecido apenas por meio de uma declaração direta.
Quando você está retornando um tipo incompleto X f2();
, está dizendo que o chamador deve ter a especificação completa do tipo X. Eles precisam dele para criar o LHS ou o objeto temporário no site da chamada.
Da mesma forma, se você aceitar um tipo incompleto, o chamador deverá ter construído o objeto que é o parâmetro. Mesmo se esse objeto foi retornado como outro tipo incompleto de uma função, o site de chamada precisa da declaração completa. ou seja:
class X; // forward for two legal declarations
X returnsX();
void XAcceptor(X);
XAcepptor( returnsX() ); // X declaration needs to be known here
Eu acho que existe um princípio importante de que um cabeçalho deve fornecer informações suficientes para usá-lo sem uma dependência que exija outros cabeçalhos. Isso significa que o cabeçalho deve poder ser incluído em uma unidade de compilação sem causar um erro no compilador quando você usa as funções declaradas.
Exceto
Se essa dependência externa é o comportamento desejado . Em vez de usar a compilação condicional, você pode ter um requisito bem documentado para que eles forneçam seu próprio cabeçalho declarando X. Essa é uma alternativa ao uso de #ifdefs e pode ser uma maneira útil de introduzir zombarias ou outras variantes.
A distinção importante são algumas técnicas de modelo nas quais você NÃO deve explicitamente instancia-las, mencionadas apenas para que alguém não fique irritado comigo.