Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

There are some details I didn't understand in §7.3.1.2/3 in the C++11 Standard

§7.3.1.2/3 in the C++11 Standard (emphasis are mine):

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a nonlocal class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function 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. [ Note: The other forms of friend declarations cannot declare a new member of the innermost enclosing namespace and thus follow the usual lookup rules.

Example:

// Assume f and g have not yet been defined.
void h(int);
template <class T> void f2(T);
namespace A {
    class X {
        friend void f(X); // A::f(X) is a friend
        class Y {
            friend void g(); // A::g is a friend
            friend void h(int); // A::h is a friend
            // ::h not considered
           friend void f2<>(int); // ::f2<>(int) is a friend
        };
    };
    // A::f, A::g and A::h are not visible here
    X x;
    void g() { f(x); } // definition of A::g
    void f(X) { /* ... */} // definition of A::f
    void h(int) { /* ... */ } // definition of A::h
    // A::f, A::g and A::h are visible here and known to be friends
}
using A::x;
void h() {
    A::f(x);
    A::X::f(x); // error: f is not a member of A::X
    A::X::Y::g(); // error: g is not a member of A::X::Y
}

Unless I'm missing something, I don't understand the need for the words first above. As far as I can see, you can't have more than one declaration of any entity in a namespace, nor more than one declaration of a friend function in a class.

Also, what is the relevance of the comment "Assume f and g have yet not been defined" in the Example? It really doesn't matter if these functions are declared before the definition of the namespace A. They'll necessarily belong to the global namespace and they'll have nothing to do with the functions declared inside the namespace A.

Edit:

The fact that one can have repeated declarations of the same function, or a declaration and a definition of a function in a namespace, doesn't invalidate my observation that the use of the words first in §7.3.1.2/3 are not necessary.

Edit1

I've just found another error. The comment ::f2<>(int) is a friend is incorrect. Not only there is no definition of the template function f2(T) in namespace A, but more important, the declaration template <class T> void f2(T); must be inside A, otherwise the function f2<>(int) will not be a friend of the class A::X::Y.

like image 356
Wake up Brazil Avatar asked Apr 15 '14 11:04

Wake up Brazil


3 Answers

A hopefully shorter more concise answer than the complete one from Vlad:

An entity can be declared multiple times, your premise is wrong. In the first sentence first is important as these two are valid declarations for a function f in namespace N:

namespace N { void f(); }
void N::f() { ... }        // A definition is *also* a declaration

At this point the need for first in the first sentence is apparent, f is a member of the N namespace (first declaration), not the global namespace.

In the case of a friend declaration the first is important for a different reason, as if the friend declaration is the first declaration, the name is not visible for regular lookup:

//[1]
class Y {};                       // Some type
class X {
   X(Y);                          // allow implicit conversions, 
                                  //    for exposition purposes
   friend X operator+(X, X) {...} // *first* declaration of this operator
};                                //    and also the definition
void f() {
   Y a, b;
   a + b;                         // error, no operator+ takes two Y
   X c; 
   c + b;                         // OK, ADL can find it
}

If the friend declaration was not the first declaration, i.e. if [1] gets replaced with a previous declaration:

class X;
X operator+(X,X);

With all the rest of the code being the same, the above code would compile and call operator+(X,X) converting a and b to X.

The last question you had was on the Assume f and g have not been defined, which I believe should read declared, not defined. The importance of this statement is that if the function have been declared before hand, the comment // A::f, A::g and A::h are not visible here becomes false, as the previous declaration makes those functions visible.

like image 153
David Rodríguez - dribeas Avatar answered Sep 22 '22 13:09

David Rodríguez - dribeas


You are wrong. You may have several declarations of the same function in a declarative region. For example

namespace N
{
   void f( int[10] );
   void f( int[10] );
   void f( int[] );
   void f( int[] );
   void f( int * );
   void f( int * );
}

All these declarations declare the same (and one) function f.

In the quote you cited word first means that a friend function was first declared inside a class definition. There is no function declaration before the declaration inside the class definition.

As for the comment

// Assume f and g have not yet been defined

then it means that the functions were not declared yet. That is their first declarations are inside the class definition.

This part of the quote

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a nonlocal class first declares a class or function the friend class or function is a member of the innermost enclosing namespace

is very clear, Declaring a function as a friend function of a class inside the class definition means its declaration inside the enclosing namespace. However the function is not visible until it will be declared also outside the class definition.

To demonstrate the idea consider the following example.

#include <iostream>

struct A {
    friend void f();
};
void g() { f(); }
void f() { std::cout << "It is me!" << std::endl; }

int main() {
    return 0;
}

For this code the compiler issues error

prog.cpp: In function ‘void g()’: prog.cpp:9:14: error: ‘f’ was not declared in this scope void g() { f(); }

Though function f was declared inside class A definition. And if there is no any declaration of the function before the class definition the function is considered as a member of the same namespace where class A is defined (more precisely it would be better to say where the class is declared because the class can be defined in some enclosing namespace).

However if you add one more declaration of f then the code will be compiled.

#include <iostream>

struct A {
    friend void f();
};

void f();
void g() { f(); }
void f() { std::cout << "It is me!" << std::endl; }

int main() {
    return 0;
}

And consider a third example

#include <iostream>

void f();
namespace N {
   struct A
   {
      friend void f();
   };
   void g() { f(); }
}
void f() { std::cout << "It is me!" << std::endl; }
int main() {
    return 0;
}

Here function f was first declared before namespace N and before the class definition. So the friend declaration of the function f will not be visible in function g() in that namespace until the friend function will be redeclared outside the class. So function g will call the global function f.

And at last consider a more interesting example

#include <iostream>

namespace N {
   class A;
}
class N::A {
    friend void f();
    int x = 10;
};

namespace N {
   //void f(); // if to uncomment the line the code will be compiled 
               // otherwise f will not be found
   void g() { f(); }
   void f() { A a; std::cout << "a.x = " << a.x << std::endl; }
}
int main() {
    return 0;
}

Here though the definition of class A is in the global namespace nevertheless function f is declared in namespace N that is where the class was first declared.

like image 41
Vlad from Moscow Avatar answered Sep 24 '22 13:09

Vlad from Moscow


Forgive me for omitting the examples, but I want to keep this short and sweet.

The purpose of the word first is to emphasize that the friend or namespace-scope declaration is the only way the function has been declared at that point. The paragraph is intending to discuss declarations that create an entity, as opposed to later matching declarations.

Perhaps initially would be a better word than first.

The comment ::f2<>(int) is a friend is incorrect. Not only there is no definition of the template function f2(T) in namespace A, but more important, the declaration template <class T> void f2(T); must be inside A, otherwise the function f2<>(int) will not be a friend of the class A::X::Y.

::f2 is a different thing than A::f2. The intent of the example is to emphasize this. Because there is a template ::f2, not in A, which matches the friend void f2<>(int) declaration, that template is the one befriended. Only because there is no visible function f matching the declaration friend void f(X); does it by default create a new function in namespace A.

The rule is quite intricate, however. The last sentence before the note means that a function ::f would still not match friend void f(X); because the language prefers to add something to the enclosing namespace, and only looks at outer namespaces as a last resort. So it goes like this:

  1. If the enclosing namespace contains a declaration matching the friend, friend that thing.
  2. Otherwise, if the friend declaration contains sufficient information to declare something in the enclosing namespace, create the thing but don't let the declaration be found by qualified or unqualified lookup.
  3. Otherwise, look for a matching declaration in outer namespaces.
  4. Otherwise, fail.
like image 21
Potatoswatter Avatar answered Sep 21 '22 13:09

Potatoswatter