5

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.

In silico
  • 51,091
  • 10
  • 150
  • 143
  • Please post what error you are getting. – Björn Pollex Jul 13 '10 at 08:04
  • I am on Mac OS X, using g++ to compile this code, Error message is, try1.cpp: In function ‘int pqr(abc)’: try1.cpp:6: error: ‘t’ has incomplete type try1.cpp:4: error: forward declaration of ‘struct abc’ try1.cpp:16: error: non-local function ‘int pqr(pqr(abc)::abc)’ uses local type ‘pqr(abc)::abc’ try1.cpp: In function ‘int main()’: try1.cpp:24: error: aggregate ‘abc t’ has incomplete type and cannot be defined – Shubhendra Singh Jul 13 '10 at 08:20
  • 2
    Are you sure you can't cope with x being public ? The class definition will not be visible outside `pqr` anyway. – Alexandre C. Jul 13 '10 at 08:34

4 Answers4

3

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
  • 7,001
  • 2
  • 21
  • 29
  • This code generates following error, try1.cpp: In function ‘int pqr()’: try1.cpp:10: error: ‘int pqr()::abc::x’ is private try1.cpp:16: error: within this context – Shubhendra Singh Jul 13 '10 at 08:26
  • This fails for me on Snow Leopard with GCC 4.2.1. (`'int pqr()::abc::x' is private within this context`). – Björn Pollex Jul 13 '10 at 08:27
  • 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 Jul 13 '10 at 08:42
  • Comeau compiler complains with `error: nonstandard local-class friend declaration -- no prior declaration in the enclosing scope` – David Rodríguez - dribeas Jul 13 '10 at 10:36
  • Why does this mean "out of luck"? A function prototype can be declared at function scope, so wouldn't this work? `void f() {void f(); class A { friend void f(); }; }` (Admittedly it's annoying to have to declare the function three times though...) – celticminstrel Apr 26 '17 at 22:53
2

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.
  • 287,565
  • 48
  • 449
  • 722
  • Why on earth are you still using GCC 3.4 instead of 4.5? – Puppy Jul 13 '10 at 09:51
  • 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 Jul 13 '10 at 10:11
  • @Elemental: Yeah, prototyping pqr() and friending it via ::pqr() was the first thing I tried after the problem was pointed out. No dice. – Gunslinger47 Jul 13 '10 at 12:01
  • @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. Jul 14 '10 at 06:36
  • "a variety of compilers do not": the standard forbids it. – Alexandre C. Feb 03 '11 at 22:23
  • @Alexandre C: I could not find this restriction in `n3225`. I found that local classes cannot have static data members for example but nothing actually preventing their usage in the STL algorithms. Could you point me to the relevant section ? – Matthieu M. Feb 04 '11 at 07:34
  • @Matthieu M. 14.3.1: *A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template type-parameter.* Local classes have no linkage. – Alexandre C. Feb 04 '11 at 08:02
1

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.

Community
  • 1
  • 1
Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • +1. I only just noticed that you beat me to the IBM article by an hour. – Gunslinger47 Jul 13 '10 at 09:52
  • 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 Jul 13 '10 at 10:39
0

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
  • 51,091
  • 10
  • 150
  • 143
  • -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). – Björn Pollex Jul 13 '10 at 08:15
  • But isnt this simply Friend function? There isn't any Local classes involed? – Shubhendra Singh Jul 13 '10 at 08:16
  • @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 Jul 13 '10 at 08:22
  • 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 private try1.cpp:19: error: within this context try1.cpp: In function ‘int main()’: try1.cpp:24: error: aggregate ‘abc t’ has incomplete type and cannot be defined – Shubhendra Singh Jul 13 '10 at 08:24
  • @Shubhendra Singh - My fault, the `friend` declaration was wrong. I have fixed it. – In silico Jul 13 '10 at 08:27
  • @silico with the recently edited code, I am getting error: ‘int pqr()::abc::x’ is private – Shubhendra Singh Jul 13 '10 at 08:29
  • -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 Jul 13 '10 at 08:30
  • @Elemental - ... which is why I have created an example involving local classes. – In silico Jul 13 '10 at 08:34