views:

198

answers:

4

I am reading "Local Classes" concept in Object-oriented programming with C++ By Balagurusamy (http://highered.mcgraw-hill.com/sites/0070593620/information_center_view0/).

The last line says "Enclosing function cannot access the private members of a local class. However, we can achieve this by declaring the enclosing function as a friend."

Now I am wondering how the highlighted part can be done?

Here is the code I was trying but no luck,

#include<iostream>
using namespace std;

class abc;

int pqr(abc t)
{
    class abc
    {
        int x;
    public:
        int xyz()
        {
            return x=4;
        }
        friend int pqr(abc);
    };
    t.xyz();
    return t.x;
}

int main()
{
    abc t;
    cout<<"Return "<<pqr(t)<<endl;
}

I know the code looks erroneous, any help would be appreciable.

A: 

The friend int pqr(abc); declaration is fine. It doesn't work because the abc type has not been defined before you used it as a parameter type in the pqr() function. Define it before the function:

#include<iostream> 
// By the way, "using namespace std" can cause ambiguities.
// See http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.5
using namespace std;

// Class defined outside the pqr() function.
class abc 
{ 
    int x; 
public: 
    int xyz() 
    { 
        return x=4; 
    } 
    friend int pqr(abc);
}; 

// At this point, the compiler knows what abc is.
int pqr(abc t) 
{ 
    t.xyz(); 
    return t.x; 
} 

int main() 
{ 
    abc t; 
    cout<<"Return "<<pqr(t)<<endl; 
}

I know you want to use a local class, but what you have set up will not work. Local classes is visible only inside the function it is defined in. If you want to use an instance of abc outside the pqr() function, you have to define the abc class outside the function.

However, if you know that the abc class will be used only within the pqr() function, then a local class can be used. But you do need to fix the friend declaration a little bit in this case.

#include<iostream> 
// By the way, "using namespace std" can cause ambiguities.
// See http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.5
using namespace std;

// pqr() function defined at global scope
int pqr() 
{
    // This class visible only within the pqr() function,
    // because it is a local class.
    class abc
    { 
        int x; 
    public: 
        int xyz() 
        { 
            return x=4; 
        } 
        // Refer to the pqr() function defined at global scope
        friend int ::pqr(); // <-- Note :: operator
    } t;
    t.xyz(); 
    return t.x;
}

int main() 
{ 
    cout<<"Return "<<pqr()<<endl; 
}

This compiles without warnings on Visual C++ (version 15.00.30729.01 of the compiler).

In silico
-1 The OP explicitly states that he want to find a solution that defines that class inside the function (if it was suggested in that book he quotes, there should be a way to accomplish that).
Space_C0wb0y
But isnt this simply Friend function? There isn't any Local classes involed?
Shubhendra Singh
@Space_C0wb0y - I realize that, but the code snippet provided by the OP will never work, because the code is declaring an `abc` instance outside the `pqr()` function, and local classes are visible only within the function they are defined in. I have edited my answer to be a bit clearer about this.
In silico
But even with the suggested code, I get errors. try1.cpp: In function ‘int pqr()’:try1.cpp:16: error: non-local function ‘int pqr(pqr()::abc)’ uses local type ‘pqr()::abc’try1.cpp:10: error: ‘int pqr()::abc::x’ is privatetry1.cpp:19: error: within this contexttry1.cpp: In function ‘int main()’:try1.cpp:24: error: aggregate ‘abc t’ has incomplete type and cannot be defined
Shubhendra Singh
@Shubhendra Singh - My fault, the `friend` declaration was wrong. I have fixed it.
In silico
@silico with the recently edited code, I am getting error: ‘int pqr()::abc::x’ is private
Shubhendra Singh
-1 he clearly wants to know about locally defined classes and modding his example to avoid any local classes doesn;t really help him much
Elemental
@Elemental - ... which is why I have created an example involving local classes.
In silico
+3  A: 

Your friend statement is fine.

int pqr() {
    class abc {
        int x;
    public:
        abc() : x(4) { }
        friend int pqr();
    };
    return abc().x;
}

int main() {
    cout << "Return " << pqr() << endl;
}

Edit:
IBM offers this explanation for the issue raised in the comments:

If you declare a friend in a local class, and the friend's name is unqualified, the compiler will look for the name only within the innermost enclosing nonclass scope. [...] You do not have to do so with classes.

void a();

void f() {
  class A {
    // error: friend declaration 'void a()' in local class without prior decl...
    friend void a();
  };
}

friend void a(): This statement does not consider function a() declared in namespace scope. Since function a() has not been declared in the scope of f(), the compiler would not allow this statement.

Source: IBM - Friend scope (C++ only)

So, you're out of luck. Balagurusamy's tip only works for MSVC and similar compilers. You could try handing off execution to a static method inside your local class as a work-around:

int pqr() {
    class abc {
        int x;
    public:
        abc() : x(4) { }
        static int pqr() {
            return abc().x;
        }
    };
    return abc::pqr();
}
Gunslinger47
This code generates following error,try1.cpp: In function ‘int pqr()’:try1.cpp:10: error: ‘int pqr()::abc::x’ is privatetry1.cpp:16: error: within this context
Shubhendra Singh
This fails for me on Snow Leopard with GCC 4.2.1. (`'int pqr()::abc::x' is private within this context`).
Space_C0wb0y
Works in VC++6 and 2010. Just tried it with g++ and it fails as you report. `error: friend declaration 'int pqr()' in local class without prior declaration`
Gunslinger47
Comeau compiler complains with `error: nonstandard local-class friend declaration -- no prior declaration in the enclosing scope`
David Rodríguez - dribeas
+1  A: 

I have no solution for the friend thing yet (don't even know if it can be done), but read this and this to find out some more about local classes. This will tell you that you cannot use local classes outside the function they are defined in (as @In silico points out in his answer.)

EDIT It doesn't seem possible, as this article explains:

The name of a function first introduced in a friend declaration is in the scope of the first nonclass scope that contains the enclosing class.

In other words, local classes can only befriend a function if it was declared within their enclosing function.

Space_C0wb0y
+1. I only just noticed that you beat me to the IBM article by an hour.
Gunslinger47
I am unsure of the answer, as that quote is about a function *first introduced*. If you had: `void foo(); void foo() { class test { friend ::foo(); }; }` then `::foo()` has been introduced before the friend declaration. I still believe that to be wrong, as when in doubt I trust the Comeau compiler and it does not compile the code.
David Rodríguez - dribeas
+2  A: 

There seems to be a misunderstand about local classes.

Normally there are here to help you within the function... and should NOT escape the function's scope.

Therefore, it is not possible for a function to take as an argument its own local class, the class simply isn't visible from the outside.

Also note that a variety of compilers do not (unfortunately) support these local classes as template parameters (gcc 3.4 for example), which actually prevents their use as predicates in STL algorithms.

Example of use:

int pqr()
{
   class foo
   {
     friend int pqr();
     int x;
     foo(): x() {}
   };

   return foo().x;
}

I must admit though that I don't use this much, given the restricted scope I usually use struct instead of class, which means that I don't have to worry about friending ;)

Matthieu M.
Why on earth are you still using GCC 3.4 instead of 4.5?
DeadMG
As people have suggested this only works on MS compilers (and generates a warning on MSVC2008). I note that: friend int ::pqr();eliminates this warning and wonder if such a construct will work on gcc as the specification quoted by Gunslinger seems to imply this.
Elemental
@Elemental: Yeah, prototyping pqr() and friending it via ::pqr() was the first thing I tried after the problem was pointed out. No dice.
Gunslinger47
@DeadMG: company policy, we are slowly migrating toward gcc 4.x but this requires a careful evaluation of its quality. It's a long process ;)
Matthieu M.