34

I have the following code:

namespace A {
    struct Foo {
        int a;
    };
}

struct Foo {
    int b;
};

struct Bar : public A::Foo {
    Bar(Foo foo) {
        c = foo.b;
    }
    int c;
};

C++ compilers complains at "c = foo.b" because A::Foo does not have a member named b. If I change the type of Bar parameter with ::Foo it works.

My question is what is the rational behind this behaviour (I suppose it has to do with the fact that the inheritance makes Bar enter the A namespace but I cannot find any documentation to support this theory.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 8
    I *think* it's argument dependent lookup related. I've tagged "language-lawyer" as I think you're after answers that reference the language standard. And a very good first question! Makes it all worthwhile. – Bathsheba Nov 28 '19 at 16:05
  • It does not enter the namespace `A`, which you can see if you let `Bar` inherit from another struct in `A`. Then there is no ambiguity. It is more like the inheritance adds everything from `A::Foo` to `Bar` including the resolution of `Foo` to `A::Foo`. Sorry, I cannot really express it more precise. – n314159 Nov 28 '19 at 16:06
  • @Bathsheba Do you mean argument type dependent name lookup for finding function names (or function templates names) or dependent names in templates? – curiousguy Nov 29 '19 at 03:39

2 Answers2

23

Every class has its name injected into it as a member. So you can name A::Foo::Foo. This is called the injected class name.

[class]

2 A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name.

[basic.lookup]

3 The injected-class-name of a class is also considered to be a member of that class for the purposes of name hiding and lookup.

Because unqualified name lookup of the argument type begins in the scope of the class Bar, it will continue into the scope of its base class to account for any member there. And it will find A::Foo::Foo as a type name.

If you want to use the global type name, simply qualify it by its surrounding (global) namespace.

Bar(::Foo foo) {
    c = foo.b;
}

Which is doing fully qualified lookup in a scope where the injected class name doesn't appear.

For a followup "why" question see

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 5
    @TedLyngmo - ADL happens with function calls, nothing relevant in those specific passages. – StoryTeller - Unslander Monica Nov 28 '19 at 16:12
  • 3
    This leads to the very amusing `struct Bar:: A::Foo::Foo::Foo::Foo::Foo {};` but [there are contexts](http://eel.is/c++draft/basic.lookup.qual#class.qual-2) where `A::Foo::Foo` designates the constructor and thus there you can't continue to add as many `Foo` as you want. This is similar (but with a completely different mechanism) to the fact that you can call a function `f` this way: `(************f)()`. – AProgrammer Nov 28 '19 at 16:21
  • @AProgrammer - Indeed. And one can construct even [more amusing examples](https://stackoverflow.com/questions/47546616/). – StoryTeller - Unslander Monica Nov 28 '19 at 16:23
  • This answer certainly explains the "what". Can it be improved to add the "why"? As in, what is the purpose of this rule? Which use cases does it improve or make possible? – davidbak Nov 29 '19 at 05:47
  • @davidbak - Why what? Why there's an injected class name? That wasn't part of this question. But it certainly is the subject of [this one](https://stackoverflow.com/questions/25549652/why-is-there-an-injected-class-name). – StoryTeller - Unslander Monica Nov 29 '19 at 06:16
2

Not a complete answer, only code that shows (since it compiles) that Bar does not enter the namespace A. You can see that when inheriting from A::Foo1 there is no problem with ambiguity of Foo which would be different if this inheritance lets Bar enter A.

namespace A {
    struct Foo {
        int a;
    };

    struct Foo1 {
        int a;
    };
}

struct Foo {
    int b;
};

struct Bar : public A::Foo1 {
    Bar(Foo foo) {
        c = foo.b;
    }
    int c;
};

n314159
  • 4,990
  • 1
  • 5
  • 20