28

The following code doesn't compile:

struct X {
  friend class Y;
  Y* ptr;
};

The cppreference describes the situation as

... If the name of the class that is used in the friend declaration is not yet declared, it is forward declared on the spot.

If the "spot" means where the friend relationship is declared, then it should be fine to declare the member Y* ptr. Why doesn't it compile? Where in the standard prohibits this?

Kan Li
  • 8,557
  • 8
  • 53
  • 93

3 Answers3

30

This is a mistake on the site. It contradicts the standard, which says that friendship declaration is not a substitute for a forward declaration:

7.3.1.2.3 Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class, function, class template or function template the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup or qualified lookup.

The part about the name not being visible to unqualified or qualified lookup essentially means that the name does not behave like a forward declaration.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • So if you put `class Y;` before the `friend class Y;` at that point the class `Y` is declared and can then be used. – Richard Chambers Aug 27 '18 at 18:32
  • @RichardChambers but it would declare class Y that is local to X, not Y in the surrounding namespace. – n. m. could be an AI Aug 27 '18 at 18:33
  • @n.m. so the `class Y;` would need to be at whatever scope the class definition is located. – Richard Chambers Aug 27 '18 at 18:34
  • @RichardChambers, yes. However, IMO, it's poor coding practice to use the `friend` declaration to also declare a class. It will be better to delcare the class first before using the `friend` declaration. – R Sahu Aug 27 '18 at 18:36
  • @RichardChambers Yes, but you have to be careful: putting `class Y;` inside `X` declares `class X::Y`, i.e. a local class, not a global class, i.e. `class ::Y`. To see an example of what I mean fork from [this demo on ideone](https://ideone.com/B7nKtX), then uncomment line 19, and compile again; you'll see a compile error. – Sergey Kalinichenko Aug 27 '18 at 18:37
  • 4
    I suspect what the quoted sentence is trying to say is that you don't need a forward declaration in order to declare a friend, it's implicitly forward-declared for that purpose. – Barmar Aug 27 '18 at 20:41
2

In addition to the answer of @dasblinkenlight, a note in 3.3.2 Point of declaration lit. 10 explicitly says that a friend declaration does not introduce (and therefore not "forward declare") a class name:

10 [ Note: Friend declarations refer to functions or classes that are members of the nearest enclosing namespace, but they do not introduce new names into that namespace ([namespace.memdef]).

Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
0

One way to fix this code is to simply add class keyword:

struct X {
    friend class Y;
    class Y* ptr;
};

This will forward declare class Y at the global scope, if X is in the global scope.

Evg
  • 25,259
  • 5
  • 41
  • 83
  • This will not "forward declare" anything, as it does not make it possible to use the name `Y` alone like a real declaration would. This makes `Y` a member of the global scope only if `X` is also directly in the global scope. More generally, that code will twice use the `Y` which is associated with the innermost enclosing namespace or block scope. – aschepler Aug 28 '18 at 03:50
  • @aschepler It was assumed that `X` was in the global scope, I refined my answer. Could you please explain your first sentence? What is the difference between `class Y; struct X { friend class Y; Y* ptr; };` and the code in my answer? – Evg Aug 28 '18 at 07:24