2

Why does this work:

#include "iostream"

class Something {
private:
    static int s_nIDGenerator;
    int m_nID;
    friend int main();
public:
     Something() { m_nID = s_nIDGenerator++; }
     int GetID() const { return m_nID; }
};

int Something::s_nIDGenerator;

int main() {
    Something::s_nIDGenerator = 1;

    Something cFirst;
    Something cSecond;
    Something cThird;

    using namespace std;
    cout << cFirst.GetID() << endl;
    cout << cSecond.GetID() << endl;
    cout << cThird.GetID() << endl;
    return 0;
}

it prints:

1
2
3

And this fail:

#include "iostream"

namespace test {   
    class Something {
    private:
            static int s_nIDGenerator;
            int m_nID;
            friend int main();
    public:
            Something() { m_nID = s_nIDGenerator++; }
            int GetID() const { return m_nID; }
    };
};

int test::Something::s_nIDGenerator;

int main() {
    using namespace test;
    Something::s_nIDGenerator = 1;
    // or test::Something::s_nIDGenerator = 1;  same effect if not using using.

    Something cFirst;
    Something cSecond;
    Something cThird;

    using namespace std;
    cout << cFirst.GetID() << endl;
    cout << cSecond.GetID() << endl;
    cout << cThird.GetID() << endl;
    return 0;
}

With the compiler error message of:

**** Internal Builder is used for build               ****
g++ -O0 -g3 -Wall -c -fmessage-length=0 -o src\tuttest1.o ..\src\tuttest1.cpp
..\src\tuttest1.cpp: In function 'int main()':
..\src\tuttest1.cpp:23:5: error: 'int test::Something::s_nIDGenerator' is private
..\src\tuttest1.cpp:27:13: error: within this context
Build error occurred, build is stopped
Time consumed: 161  ms. 

How do I get the 2nd example to work using the namespace test?

How/Why is the namespace declaration around the object preventing the static member form being accessible?


Per my comment to @zmo, here is what I got to work based on his clue:

(comment doesn't have the space or formatting for this, and I had to edit because I couldn't set this an answer.... (what ever it takes.)

#include "iostream"

namespace test {
    class Something {
    private:
        static int s_nIDGenerator;
        int m_nID;
        friend void load(int);
    public:
        Something() { m_nID = s_nIDGenerator++; }
        int GetID() const { return m_nID; }
    };

    int Something::s_nIDGenerator;

    void load (int value) {
       Something::s_nIDGenerator = value;
    } 

};

int main() {
    using namespace test;
    load (1);

    Something cFirst;
    Something cSecond;
    Something cThird;

    using namespace std;
    cout << cFirst.GetID() << endl;
    cout << cSecond.GetID() << endl;
    cout << cThird.GetID() << endl;
    return 0;
}

I am still a little loose as to the "what's up with static members being in a class and a namespace not working?" What's up with this? Why didn't test::Something::s_nIDGenerator work? (still a part of my original question.) So, we are half-answered, so far.

I want to know why this didn't work so I don't walk into this rake again.

Quade2002
  • 615
  • 2
  • 7
  • 23

2 Answers2

3

Probably because your friend int main() declaration is declaring that the namespace also has a free main() function, while the real main() function is not in the namespace.

To fix it? First declare int main(); before (and outside) namespace test, then friend int ::main() to indicate it's in the global namespace.

For more details, see this question.

Community
  • 1
  • 1
Rob I
  • 5,627
  • 2
  • 21
  • 28
  • 1
    and though it may work, it's not a good idea to make ::main() friend of a class. You shall better do your ::main() friend code inside a function that's inside your class, and call it from ::main(). my 2 cents. – zmo May 20 '12 at 14:02
  • @zmo THIS is it, Thank You! You gave me the clue I needed. Putting a function that is inside the class that has access to the static variable. I tired this and it worked in both my test example and back in my real project. (still doesn't give me a "why"... just a "what" on how to fix it. I will post it as an answer but will tag an 'accepted' if someone can explain this. (obviously my learning materials don't cover the 'whys' of things enough.) – Quade2002 May 20 '12 at 14:35
  • I guess you don't get to see it..... :( "Oops! Your answer couldn't be submitted because: Users with less than 100 reputation can't answer their own question for 8 hours after asking. You may self-answer in 7 hours. Until then please use comments, or edit your question instead." – Quade2002 May 20 '12 at 14:44
  • This: "7.3.1.2 Namespace member definitions Paragraph 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 or function (this implies that the name of the class or function is unqualified) the friend class or function is a member of the innermost enclosing namespace. From that post is the reason." looking at this closely, this is why it was tagging it as private. This is my WHY. It wasn't that it was inaccessible by name, it was because what was trying to use it wasn't declared in the proper scope. – Quade2002 May 20 '12 at 15:19
1

well, though I will never recommand you to do like you did in your question, here is how to make your code work "as is" :

#include <iostream>

int main(); // declare main beforehands so it can be seen by Something

namespace test {   
    class Something {
    private:
            static int s_nIDGenerator;
            int m_nID;
            friend int ::main(); // take the main from global namespace
    public:
            Something() { m_nID = s_nIDGenerator++; }
            int GetID() const { return m_nID; }
    };
};

int test::Something::s_nIDGenerator;

int main() {
    using namespace test;
    Something::s_nIDGenerator = 1; // tada that works

    Something cFirst;
    Something cSecond;
    Something cThird;

    using namespace std;
    cout << cFirst.GetID() << endl;
    cout << cSecond.GetID() << endl;
    cout << cThird.GetID() << endl;
    return 0;
}

but here is a wrong use case of a friend function. The solution that seemed to work for you that I suggested (use a function inside your class Something) is far better for readability and understandability.

zmo
  • 24,463
  • 4
  • 54
  • 90
  • as a side note, the whys : when you do "friend int main()", the compiler thinks you're declaring a main() function in test namespace that will get defined later on (either later in the same object, or through linkage) ; then it complains in the real main() that you don't have access to private members. – zmo May 20 '12 at 14:48
  • When you do "friend int ::main()" the compiler will complain "error: 'int main()' should have been declared inside '::'" which means the compiler never heard about a "::main()" whatsoever, because you're taking an object from another namespace, and you cannot declare new prototypes from one namespace to another, that's why you need to declare void main(); – zmo May 20 '12 at 14:50
  • I edited my original question, your version works, I see the forward declaration of main. I tried your idea (based on your comment.) It does work. (I wasn't fond of what I was trying to do either... just trying to find a reasonable solution.) The idea you suggested also worked. This whole static member inside a class inside a namespace is not so strait forward. Thanks!!!! – Quade2002 May 20 '12 at 15:05