61

I have a class A which has two static variables. I'd like to initialize one with another, unrelated static variable, just like this:

#include <iostream>
class A
{
public:
    static int a;
    static int b;
};

int A::a = 200;
int a = 100;
int A::b = a;
int main(int argc, char* argv[])
{
    std::cout << A::b << std::endl;

    return 0;
}

The output is 200. So, could anyone tell me why?

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
QuantumPlus
  • 473
  • 3
  • 6
  • 1
    I'm no language expert, but my best guess is that as soon as you write `A::b`in your assignment statement, you're scoping to the `A` class and the `a` there. To access the global `a`, it should be something like `int A::b = ::a;`. – JHBonarius Jul 11 '18 at 07:15
  • 15
    @AmirRasulov: Well, thus far 11 people (including me) think the question shows research effort, is useful, and is clear. – Bathsheba Jul 11 '18 at 07:36
  • 13
    @AmirRasulov - This is a Q&A site. The threshold for a Q to belong here is its objective quality (like Bathsheba pointed out), not how simple the answer may sound to you or me. – StoryTeller - Unslander Monica Jul 11 '18 at 07:45
  • 7
    @AmirRasulov You could use that argument for every question on this site... – Rakete1111 Jul 11 '18 at 08:21
  • @AmirRasulov (0) It's on-topic. (1) It is simple ⇒ understood by many people. (so, unfortunately, if it's in an obscure topic then it will get less upvotes, no matter how well-researched it is) **(2)** It's on HNQ. – user202729 Jul 12 '18 at 07:45

4 Answers4

36

That's correct according to the lookup rules. [basic.lookup.unqual]/13 says:

A name used in the definition of a static data member of class X (after the qualified-id of the static member) is looked up as if the name was used in a member function of X. [ Note: [class.static.data] further describes the restrictions on the use of names in the definition of a static data member.  — end note ]

Since the unqualified a is looked up as if you are inside a member function, it must find the member A::a first. The initialization order of A::a and A::b doesn't affect the lookup, though it affects how well defined the result is.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 4
    Can anybody give any reasons why the behavior was decided like this? I find the code of the question highly confusing. – Chiel Jul 11 '18 at 07:32
  • 1
    @Chiel: Facetiously I think it's the simplest consistent way of resolving this ambiguity. – Bathsheba Jul 11 '18 at 07:34
  • 2
    @Bathsheba. But the rule makes the ambiguity rather than resolves it as far as I understand it. – Chiel Jul 11 '18 at 07:37
  • 2
    @Chiel - Name lookup has to start at *some scope*, this one is just a sensible default. Imagine we have an `enum class` in our class, and we want to to use one of its enumerators to initialize the static member. You'd more naturally write `A::a = EnumClass::THING;` and not `A::a = A::EnumClass::THING;`. – StoryTeller - Unslander Monica Jul 11 '18 at 07:38
  • @Chiel: I think once you study the link in this answer, and the rules for argument dependent lookup, it all starts to *feel* consistent. That said, I didn't submit an answer as it would have been vague compared to this one in particular. – Bathsheba Jul 11 '18 at 07:39
  • 5
    @Chiel - I'd also like to add another thing. It's not the rule which made this ambiguous. The rules disambiguate it quite well. It's the programmer who chose poor names that makes this ambiguous. – StoryTeller - Unslander Monica Jul 11 '18 at 07:41
  • 1
    Thank you very much! This question is really ambiguous to me. – QuantumPlus Jul 11 '18 at 08:49
16

So, could anyone tell me why?

This is clearly stated in basic.scope.class/4, emphasis mine:

The potential scope of a declaration that extends to or past the end of a class definition also extends to the regions defined by its member definitions, even if the members are defined lexically outside the class (this includes static data member definitions, nested class definitions, and member function definitions, including the member function body and any portion of the declarator part of such definitions which follows the declarator-id, including a parameter-declaration-clause and any default arguments).

Thus, when you have

int A::a = 200;
int a = 100;
int A::b = a; // note the '::' scope resolution operator
              // OUTPUT: 200

a actually refers to A::a because the class scope is extended by A::b.

Unlike if you have:

int A::a = 200;
int a = 100;
int b = a; // note b is not A::b
           // i.e. without the '::', scope resolution operator
           // OUTPUT: 100

a would refer to the (global) ::a since b here is not a member of class A,
i.e. no class scope extension.

Joseph D.
  • 11,804
  • 3
  • 34
  • 67
  • Thank you very much for your answer. – QuantumPlus Jul 11 '18 at 08:51
  • @QuantumPlus [Just upvote instead](https://meta.stackexchange.com/q/126180). "Thank you" comments are *discouraged*. Also, if you see such comments, you can [flag them](https://meta.stackexchange.com/questions/17364/how-does-comment-voting-and-flagging-work). – user202729 Jul 12 '18 at 07:46
8

c++draft/class.static

If an unqualified-id is used in the definition of a static member following the member's declarator-id, and name lookup ([basic.lookup.unqual]) finds that the unqualified-id refers to a static member, enumerator, or nested type of the member's class (or of a base class of the member's class), the unqualified-id is transformed into a qualified-id expression in which the nested-name-specifier names the class scope from which the member is referenced. [ Note: See [expr.prim.id] for restrictions on the use of non-static data members and non-static member functions. — end note ]

It says the unqualified-id is transformed into a qualified-id expression in your situation.

int A::b = a;

You can set qualified-id but has no nested-name-specifier like this.

int A::b = ::a;
dao leno
  • 261
  • 2
  • 5
6

Because the name look up resolves the a as A::a. If you want to do this you will need to resolve the scope manually:

int A::b = ::a;
        // ^ Global scope resolution

Live Example

Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175