1

After reading The C++ Programming Language 4th edition (section 14.4.4 - Composition and Selection) I thought that the following program would compile correctly:

#include <iostream>

namespace foo {
    int var = 0;
    // more declarations
}

namespace bar {
    int var = 1;
    // more declarations
}

namespace baz {
    // composition of namespaces foo and bar
    using namespace foo;
    using namespace bar;
    // I thought that the following line would resolve the name clash
    using foo::var;   
}

using namespace baz;

void qux(int n) {
    if (n == var) {
        std::cout << "baz: var " << std::endl;
    } else {
        std::cout << "baz: " << n << std::endl;
    }
}

int main() {
    qux(0);
    qux(1);
}

The expected output should be

baz: var

baz: 1

When I try to compile the code above I get an error saying that the reference to 'var' is ambiguous. Using foo::var eliminates the compilation error but I would like to better understand this problem.

What is wrong in my code?

How can I resolve the name clash in a way that the namespace baz contains foo::var?

Thanks!

EDIT

As suggested in the answers below, moving the definition of qux inside the namespace baz makes it valid. Therefore, the following code compiles as expected

#include <iostream>

namespace foo {
    int end = 0;
}

namespace bar {
    int end = 1;
}

namespace baz {
    using namespace foo;
    using namespace bar;
    using foo::end;
    void qux(int n) {
        if (n == end) {
            std::cout << "baz: end " << std::endl;
        } else {
            std::cout << "baz: " << n << std::endl;
        }
    }
}

int main() {
    baz::qux(baz::end);
    baz::qux(1);
}

There is still one thing which is not completely clear to me. I thought that adding using namespace baz would make everything in baz available also in the current scope without using baz::. In other words, I would have expected that since "everything works in the namespace baz" this would be true also in the current scope after adding using namespace baz.

Community
  • 1
  • 1
carlo
  • 325
  • 1
  • 4
  • 12
  • 3
    Don't use `using namespace xxxx` – NathanOliver Feb 07 '18 at 22:21
  • `using foo::var` makes `var` accessible without namespace qualifiers. If other declarations have made `var` accessible without namespace qualifiers such as (`using namespace bar`) it does nothing to pick this one as the default or anything. – zzxyz Feb 07 '18 at 22:22
  • 1
    And while I don't agree with @NathanOliver, I am 1) outvoted by programmers who are probably better than me, and 2) I would *never* use namespace composition that could possibly have to be resolved like you are trying to do. (Even if it was possible, which it may be, although I doubt) – zzxyz Feb 07 '18 at 22:31
  • 1
    @zzxyz I've encountered a few bugs/errors when using `using namespace xxxx`. One was very unintuitive. After getting burned a couple of times I've just decide that fully qualifying the names is just safer. You can alias namespace names to make it less typing. – NathanOliver Feb 07 '18 at 22:46
  • @NathanOliver - I do the big bad: `using namespace std;` all the time. I can't *stand* how STL code looks without it. I haven't been burned yet *crosses fingers* – zzxyz Feb 07 '18 at 22:54
  • 1
    @zzxyz: `using std::string; using std::vector;` (etc.) makes STL code just as nice, and doesn't lead to name clashes you weren't aware of. – Ben Voigt Feb 07 '18 at 23:18
  • @BenVoigt `std::unordered_map>>` is honestly basically visually incomprehensible to me, where `unordered_map>>` I can immediately comprehend. (I find `typedef` distasteful for some purposes)...And forget about starting to wrap stuff in `std::shared_ptr`) – zzxyz Feb 08 '18 at 05:03
  • 1
    @zzxyz: So use the second. `using` is not evil unless it appears in global scope and is followed by `namespace`. – Ben Voigt Feb 08 '18 at 05:04
  • @BenVoigt - Thank you. Somehow that alternative had not occurred to me...What about global scope in a single compilation unit? (a .cpp/.c file vs .h)? – zzxyz Feb 08 '18 at 05:08
  • @zzxyz: A lesser evil. Have you ever tried listing the names you use from the library? You might find that list more useful than you think... and it makes the code more portable, because you aren't pulling in an unknown list of extra names. – Ben Voigt Feb 08 '18 at 05:19
  • @BenVoigt - It's pretty short, I think. 4 or 5 items that I use regularly. It's a good plan. – zzxyz Feb 08 '18 at 05:22

3 Answers3

2
using foo::var;

The line above basically does nothing except make the your function call more ambiguous. The first using namespace foo makes anything in namespace foo available without foo:: (So var). The next line does the same thing for namespace bar. The last line, the one shown above, tells the compiler to specifically remove the namespace qualifier for foo::var only inside baz. Thus, in your if statement, the compiler is given 3 candidates to choose from, and can pick none. The exact error GCC 7.2 gives is:

prog.cc: In function 'void qux(int)':

prog.cc:24:14: error: reference to 'var' is ambiguous if (n == var) {

prog.cc:4:9: note: candidates are: int foo::var int var = 0;

prog.cc:9:9: note: int bar::var int var = 1;

prog.cc:4:9: note: int foo::var int var = 0;

See it live

To avoid this ambiguity, it is best you avoid statements of the sort using namespace a; and instead fully qualify all names.

Arnav Borborah
  • 11,357
  • 8
  • 43
  • 88
2
// I thought that the following line would resolve the name clash
using foo::var;   

Well, it does resolve the name clash for the current namespace, which is baz.

If you move qux into baz, it will prefer foo::var.

But for ::qux (outside any namespace), there is no reason to prefer baz::var (the alias for foo::var) over bar::var or foo::var, since all of them are in the current namespace search path.

Even though only using namespace baz; was seen at global level.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

So problem is when you use variable name that can go from both namespaces. When you have multiple candidates at a specific level, then that is an ambiguity. You have foo::var and bar::var, and you make using namespace foo; using namespace bar;

Now when compiler see var, he can't know which var you want use. If you really want use that using namespace, then you need make an exception for these variables and say what you mean f.e:

if (n == foo::var) 
{
    std::cout << "baz: var " << std::endl;
} 
else if(n == bar::var) 
{
    std::cout << "baz: " << n << std::endl;
}