What is - if any - the difference between a forward declaration in a (template) argument (using an elaborated type specifier) and a "normal" forward declaration?
void foo(struct bar *);
// vs
struct bar;
void foo(bar *);
// ------ and also -------
std::unique_ptr<class Baz> one;
// vs
class Baz;
std::unique_ptr<Baz> two;
Let's begin by noting that "forward declaration" is a colloquialism used to refer to a certain common practical use of certain kinds of declarations. There is no such thing as a forward declaration as far as the C++ standard is concerned. There are just declarations.
With that in mind, I believe that there is no difference between
and
as far as their effect on the name
bar
is concerned. Both declarations end up introducing the name of the structbar
if there is no previous declaration that already did so.The relevant paragraph in C++17 would seem to be [basic.lookup.elab]/2 (emphasis mine):
If an elaborated-type-specifier that doesn't contain a nested-name-specifier is encountered, unqualified name lookup is performed to see if the name already names a corresponding type. If no previously declared name is found, then the elaborated-type-specifier becomes a declaration of the class type of that name…
As pointed out by geza, the one way in which there can be a difference has to do with the scope into which the name is introduced. While
always introduces the name into the scope in which the declaration appears, an elaborated-type-specifier appearing as part of any other kind of declaration will introduce the name into the closest enclosing namespace [basic.scope.pdecl]/7.