class C1 { }; // will become friend of N::C
void f1(); // will become friend of N::C
namespace N {
class C2 { }; // will become friend of C
void f2() { } // will become friend of C
class C {
int x;
public:
friend class C1; // OK (previously defined)
friend void f1();
friend class C3;
friend void f3(); // OK (defined in enclosing namespace)
friend class C4; // First declared in N and assumed to be in N
friend void f4();
};
class C3 {}; // friend of C
void f3() { C x; x.x = 1; } // OK: friend of C
} // namespace N
class C4 { }; // not friend of N::C
void f4() { N::C x; x.x = 1; } // error : x is private and f4() is not a friend of N::C
Am 25.06.2015 16:03 schrieb "Belloc" <jabe...@gmail.com>:
>
> First, let's look at these two "unfriendly" notes:
>
> [basic.scope.declarative]/4:
>
> [ Note: These restrictions apply to the declarative region into which a name is introduced, which is not necessarily the same as the region in which the declaration occurs. In particular, elaborated-type-specifiers (7.1.6.3) and friend declarations (11.3) may introduce a (possibly not visible) name into an enclosing namespace; these restrictions apply to that region. Local extern declarations (3.5) may introduce a name into the declarative region where the declaration appears and also introduce a (possibly not visible) name into an enclosing namespace; these restrictions apply to both regions. — end note ]
>
> [basic.scope.pdecl]/11:
>
> [ 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 (7.3.1.2). Function declarations at block scope and variable declarations with the extern specifier at block scope refer to declarations that are members of an enclosing namespace, but they do not introduce new names into that scope. —end note ]
>
> Now take a look at [namespace.memdef]/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 template97 the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup (3.4.1) or qualified lookup (3.4.3). [ Note: The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship). — end note ] If a friend function or function template is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.
>
> The last highlighted sentence above is superfluous, as there is no lookup for the name declared in a friend declaration in this case. What would be the purpose of this lookup anyway?
>
The issue how what this means is already covered by issue 138: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#138
Also I remember there is an issue about the conflict of there being an invisible name or not... Can't find the number currently though.
>It can become a friend, if the friend declaration uses a qualified name.
>Huh? In order to find out whether a declaration is the first
>declaration, lookup must
>be done. For an unqualified identifier, that lookup is done just for
>the enclosing
>namespace, but a lookup must happen.
>To determine whether the declaration is the first declaration, as I said.
>As an example,
namespace M
{
void f();
class X
{
friend void f(); // the lookup happens here, and finds void M::f();
};
}
as opposed to
namespace M2
{
class X
{
friend void f(); // the lookup happens here, and doesn't find anything
};
}
Because it changes the semantics of the declaration, especially if
it's a declaration
that is a definition. I know there's nothing wrong with the second
snippet, but it doesn't
have the same semantics.
Consider:
namespace N {
void f() { }
class S {
friend void f() { } // refers to N::f(); error: duplicate definition
};
}
void f() { }
namespace M {
class S {
friend void f() { } // introduces M::f() (not visible to name lookup except for ADL); ok
};
}
Jens
On 2015–06–26, at 4:33 AM, Belloc <jabe...@gmail.com> wrote:As a matter of fact, I dispute the importance of this information for the compiler, at the moment it is parsing the friend declaration, in any case. That is, even when the name in the friend declaration is qualified, or is a template-id, I believe the compiler doesn't care at this point whether the name is, or is not, first declared in its namespace. This will be taken into account only when the name is used, i.e., when the function, or the member function is called in the program. At this point, the compiler will consider only names in its innermost enclosing namespace for the cases where the name in the friend declaration is a function, or an elaborated-type-specifier, or will consider all the scopes, in and beyond, the innermost enclosing namespace, for the other two cases.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
struct Outer {
struct TheFriend;struct Inner {
friend class TheFriend;
};
};
The declaration 'friend class TheFriend' is not the first declaration for 'TheFriend', lookup finds 'Outer::TheFriend' and makes that a friend of 'Outer::Inner'. That declaration does not make 'TheFriend' a member of the innermost namespace, it is left as a member of 'Outer'.
David
In a perfect world, perhaps implementations would defer the lookup of a friend declaration until the friendship is used by overload resolution. Ideally, friendship should be specified without declarations at all. A class should be able to grant friendship without dictating anything about the befriended entity, such as its linkage, presence in an unnamed namespace, inline qualification, or exception specification. (Friend declarations which are definitions would be the only exception.) It shouldn’t matter whether or not a friend declaration lexically precedes the declaration of the befriended entity. Really what a befriending class should do is to nominate some operation or interface as privileged without caring about the implementation behind it. I don’t think C++ falls short of this by design intent, but because declarations are an approximation to some Platonic ideal of friendship.The compiler implementation practice is to resolve friendship immediately while processing the body of a class. The standard codifies the practice, perhaps deviating from the ideal. It would be nice to see a proposal describing the advantage to the model you’re advocating. It should also include analysis of the runtime complexity of the status quo and the proposed model.
struct Outer {
struct TheFriend;struct Inner {
friend class TheFriend;
};
};
The declaration 'friend class TheFriend' is not the first declaration for 'TheFriend', lookup finds 'Outer::TheFriend' and makes that a friend of 'Outer::Inner'. That declaration does not make 'TheFriend' a member of the innermost namespace, it is left as a member of 'Outer'.
David
On 2015–06–26, at 5:42 PM, David Rodríguez Ibeas <dib...@ieee.org> wrote:struct Outer {
struct TheFriend;struct Inner {
friend class TheFriend;
};
};
The declaration 'friend class TheFriend' is not the first declaration for 'TheFriend', lookup finds 'Outer::TheFriend' and makes that a friend of 'Outer::Inner'. That declaration does not make 'TheFriend' a member of the innermost namespace, it is left as a member of 'Outer’.
On 2015–06–26, at 9:16 PM, Belloc <jabe...@gmail.com> wrote:used your example to produce the following snippet (see live example), where one friend declaration in Outer::Innergenerates two befriended entities at the same time:
#include<iostream>
class Outer {
struct TheFriend;
public:
class Inner {
friend struct TheFriend;
// private:
static const int i = -1;
};
};
struct TheFriend{ void f() { std::cout << Outer::Inner::i << '\n'; } };
int main()
{
TheFriend global;
global.f();
}
On 2015–06–27, at 9:02 PM, Belloc <jabe...@gmail.com> wrote:
But what would you say about this new example I posted here. See below:
I just can't foresee any problem whatsoever, if the friendship is resolved while the compiler is parsing an access to a class member, like i in the expression std::cout << Outer::Inner::i << '\n'; above, instead of having to decide this while parsing the friend declaration friend struct TheFriend;. The compiler already has to verify whether the access to the member i is allowed, when it's parsing the alluded expression. It is just a matter of verifying additionally, at this point, whether the function f is a friend of Outer::Inner.
Could you explain to me, in simple terms, what is the problem with this approach?
On 2015–06–27, at 9:02 PM, Belloc <jabe...@gmail.com> wrote:I just can't foresee any problem whatsoever, if the friendship is resolved while the compiler is parsing an access to a class member, like i in the expression std::cout << Outer::Inner::i << '\n'; above, instead of having to decide this while parsing the friend declaration friend struct TheFriend;. The compiler already has to verify whether the access to the member i is allowed, when it's parsing the alluded expression. It is just a matter of verifying additionally, at this point, whether the function f is a friend of Outer::Inner.Even if the compiler lazily resolves the friendship, it will still choose only Outer::TheFriend and not ::TheFriend.Lazy resolution can only make a difference when the friend declaration is the initial declaration, which was the original topic of this thread.
#include<iostream>
struct Outer {
struct TheFriend{ void f() {std::cout << Inner::i << '\n'; } };
class Inner {
friend struct TheFriend;
static const int i = -1;
};
};
struct TheFriend{ void f() { std::cout << Outer::Inner::i << '\n'; } };
int main()
{
TheFriend global;
Outer::TheFriend outer;
global.f();
outer.f();
}
On 2015–06–28, at 5:18 AM, Belloc <jabe...@gmail.com> wrote:That's not what I was thinking in terms of friendship. My idea was to simply forget about whether the friend declaration is, or is not, the first declaration in its namespace. For example, in the code you posted, which I reproduced below, both member functions Outer::TheFriend::f and ::TheFriend::f would be friends of the class Outer::Inner. This is what I thought you were referring to, when you wrote the following, in your first post:"In a perfect world, perhaps implementations would defer the lookup of a friend declaration until the friendship is used by overload resolution. Ideally, friendship should be specified without declarations at all. A class should be able to grant friendship without dictating anything about the befriended entity, such as its linkage, presence in an unnamed namespace, inline qualification, or exception specification.
But now you're saying "Lazy resolution can only make a difference when the friend declaration is the initial declaration, which was the original topic of this thread.". I understand the change I'm proposing is not practical now, as this would break a lot of code already in production. What I'd like to know is, why such a proposal was not adopted when C++ was in its infant stage, as this solution seems to be so much cleaner and elegant than this horrible confusion caused by the expression "If a friend declaration in a non-local class first declares a class, function, class template or function template ..." in [namespace.memdef]/3. In other words, can you imagine any significant problem, had this hypothetical solution been adopted in place of the current solution in the Standard?
On 2015–06–29, at 6:15 PM, David Rodríguez Ibeas <dib...@ieee.org> wrote:class T { friend class U; };
namespace { class U { ... }; }
Lazy resolution here means that anyone including the header where 'T' is defined can provide their own "U" to access the internals, incidentally causing what you did not want: "I didn’t mention befriending two things in one declaration." I don't particularly care for these (befriending multiple things, or not knowing what is befriended).
In the case of a type being defined inside class, I would be fine with it as the author of the class is in full control of both entities and the friend would be resolved right away inside the same class definition. Yet the question remains, why would we special case that rather than leave it as is and have the programmer order the friend declaration and the befriended entity declaration appropriately?
Personally, I'd prefer the friend declaration not declaring anything, but forcing the existence of a previous declaration.
The problem is that at the same time, I really like being able to define a friend function inside another type for two reasons, first because the function is only available for ADL and second because with templates it might be impossible to declare the same thing before hand:
template <typename T> class U {
friend std::ostream& operator<<(std::ostream& out, U const & obj) { .. }
};
I agree with Belloc that this part of the specification might be confusing, I understand that many people get it wrong (I have made mistakes in the past similar to the examples in Bjarne's book), but the feature is there and I find it valuable. If we can make the intention clearer, I am all for it. But I'd rather not make 'friend' more diffuse by having it befriend *anything* that might come later that resembles the declaration.
Using the metaphor of frienship being you giving your keys to a friend, I'd rather name the friend than let the doorman give my keys to the first person that asks to enter my apartment.
Correct. It’s not clear how this relates to my message, though. I’m drifting off into proposal-land. I’d prefer this to be well-formed, with the same meaning:struct Outer {
struct Inner {
friend class TheFriend; // ill-formed NDR because TheFriend is redeclared in class scope.
}; // In practice, it will bind to the namespace: http://coliru.stacked-crooked.com/a/dc2be64a15818f68
struct TheFriend;
};
That's 3.3.7/1 rule 2: "A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule."
--
I am not convinced that to be the most relevant quote (it may or may not, I don't have the drive to pursue it), but the next point:3 If reordering member declarations in a class yields an alternate valid program under (1) and (2), theprogram is ill-formed, no diagnostic is required.
So either the quote provided by Richard makes it undefined behavior, or that yields a valid program but reordering of the Inner and TheFriend members yields an also valid albeit different program and you have undefined behavior there.
I am not convinced that to be the most relevant quote (it may or may not, I don't have the drive to pursue it), but the next point:3 If reordering member declarations in a class yields an alternate valid program under (1) and (2), theprogram is ill-formed, no diagnostic is required.
So either the quote provided by Richard makes it undefined behavior, or that yields a valid program but reordering of the Inner and TheFriend members yields an also valid albeit different program and you have undefined behavior there.On Tue, Jun 30, 2015 at 1:28 PM, Belloc <jabe...@gmail.com> wrote:
On Monday, June 29, 2015 at 8:44:52 PM UTC-3, Richard Smith wrote:That's 3.3.7/1 rule 2: "A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule."So, you're basically saying that the word "complete" above means that the rule also applies for any name N in S and any name N in any nested class of S. Is that correct?
[namespace.memdef]/3 contains this Note: "The other forms of friend declarations cannot declare a new member of the innermost enclosing namespace and thus follow the usual lookup rules."What other forms is this note referring to?
Look at the sentence to which the note is attached:"If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, [...]"So the other forms are the ones where:* the name is qualified, or* the name is a template-id, or* the declaration is "friend simple-type-specifier ;" or "friend typename-specifier ;" (see 11.3/3)
struct Outer {
class Inner {
friend class C;
friend void f();
static int const i = 0;
};
class C { static int const k = Inner::i; };
void f() { int i = Inner::i; }
};
class C { static int const k = Outer::Inner::i; };
void f() { int i = Outer::Inner::i; }
struct Outer {
class C {};
void f() {}
class Inner {
friend class C;
friend void f();
static int const i = 0;
};
};
class C { static int const k = Outer::Inner::i; };
void f() { int i = Outer::Inner::i; }
As was pointed out in the previous few messages, the first example is ill-formed, no diagnostic required (which basically means UB with an optional compile-time diagnostic).
struct Outer {
class C {};
void f() {}
class Inner {
friend class C;
friend void f();
static int const i = 0;
};
};
class C { static int const k = Outer::Inner::i; };
void f() { int i = Outer::Inner::i; }Now clang befriends the class Outer::C and the function ::f. Why is that?
On Tuesday, June 30, 2015 at 4:35:48 PM UTC-3, Richard Smith wrote:As was pointed out in the previous few messages, the first example is ill-formed, no diagnostic required (which basically means UB with an optional compile-time diagnostic).
- I can only think of §3.3.7/1 (3) to justify my first snippet being ill-formed. I checked the most current draft (N4527) and the bullet point (3), "If reordering member declarations in a class yields an alternate valid program under (1) and (2), the program is ill-formed, no diagnostic is required" was really eliminated from the Standard, as you mentioned before.
- But I can' t see how (2) covers this,
- as you have also mentioned above: "That text no longer exists in the standard. It was removed because (2) already covers all the cases that should be ill-formed, and (3) made many other classes ill-formed that should not be (such as "struct X { int a; int b; };").
- But let's assume for the moment bullet point 2 covers this case. Then why is my first snippet ill-formed and my second snippet well-formed? As it happens, if I change my second snippet to the one below, I would get an alternate valid program, wouldn't I?
struct Outer {
void f() {}
class Inner {
friend class C;
friend void f();
static int const i = 0;
};
class C {};
};
class C { static int const k = Outer::Inner::i; };
void f() { int i = Outer::Inner::i; }
And this would make my second snippet ill-formed too, vis-à-vis bullet point (3) that was erased from §3.3.7/1 .
--
Within the definition of "struct Outer", there is a use of the names "C" and "f". Lookup at the point where the name is used finds no declaration. Lookup in the completed scope of "struct Outer" finds Outer::C and Outer::f. So the code is ill-formed (NDR) by rule (2).
--
On 2015–07–02, at 4:19 PM, David Rodríguez Ibeas <dib...@ieee.org> wrote:I think I already answered to this yesterday, but here the reason again:
struct Outer {void f();
class Inner {
friend void f();
};
};
Outer::f is *not* a friend of 'Inner', the friend declaration refers to a free (non-member) function. If you wanted to befriend the member function you would have to type:
friend void Outer::f();
If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.
On 2015–07–02, at 4:39 PM, David Krauss <pot...@gmail.com> wrote:It’s as if namespace lookup for declarations matching a friend is ignoring non-types, although that’s probably only a coincidence.
--
I think I already answered to this yesterday, but here the reason again:
struct Outer {void f();
class Inner {
friend void f();
};
};
Outer::f is *not* a friend of 'Inner', the friend declaration refers to a free (non-member) function. If you wanted to befriend the member function you would have to type:
friend void Outer::f();
On 2015–07–02, at 7:30 PM, Belloc <jabe...@gmail.com> wrote:Definitely, I have a great deal of difficulty understanding what you write. Again, could you explain this in more simple terms?
The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11) shall be obeyed.
On 2015–07–03, at 3:23 AM, Belloc <jabe...@gmail.com> wrote:That doesn't seem to agree with [basic.lookup.unqual]/7 and 10.
Implementations have apparently converged to find member (nested) classes but not member functions. Perhaps because, once upon a time, nested classes weren’t automatically friends, so you had to manually befriend them. C++03 §11.8/1:The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11) shall be obeyed.
--
--
The missing part is that Inner's definition is inside Outer's definition, and lookup for C yields ::C inside Inner (both in the friend declaration and the end of Inner's class definition), but it would resolve to ::Outer::C when looked up at the end of Outer's scope.