How can I explicitly refer to an enclosing namespace when an inline namespace exists?
Asked Answered
P

3

25

Please consider this code:

#include <iostream>

namespace Foo{

    void ool()  // Version A
    {
        std::cout << "Foo::ool" << std::endl;
    }

    inline namespace Bar{
        void ool() // Version B
        {
            std::cout << "Foo::Bar::ool" << std::endl;
        }
    }
}


int main()
{
    Foo::ool();  // <- error
}

Both Clang and G++ correctly mark Foo::ool as ambiguous. I can call Foo::Bar::ool without problem but is there a way to call the version A without changing its declaration?

I found people in similar position trying to understand what happens but I did not see a solution for this case.

I am in this situation because I have a project that includes a declaration of std::__1::pair and std::pair, made in different places, with std::__1 being an inline namespace. I need the code to point to the std::pair explicitly. Is there a solution for that?

Passacaglia answered 25/8, 2015 at 8:29 Comment(3)
Perhaps define an alias template before introducing __1::pair that refers to std::pair?Snatchy
Are you sure you are not looking at this problem? (basically, the solution would be to add -std=c++0x (or similar with newer compilers) to the compilation options.) What compiler version and settings are you using?Avilla
utnapistim: you are correct. I had encountered this workaround for my specific case but it got me curious about how to solve this specific ambiguity in the general case. I think the solution lied more in my case in std=libstdc++ but it did solve my compilation issues, thanks!Passacaglia
M
13

I don't think that is possible; from cppreference:

Qualified name lookup that examines the enclosing namespace will include the names from the inline namespaces even if the same name is present in the enclosing namespace.

However, it seems you are not actually in the situation you describe, as you say that the two definitions are pulled from different files. Thus you "bookmark" the more external definition in order to be able to call it when you need it:

#include <iostream>

// Equivalent of first include
namespace Foo{
    void ool()  // Version A
    {
        std::cout << "Foo::ool" << std::endl;
    }
}

const auto& foo_ool = Foo::ool;

// Equivalent of second include
namespace Foo{
    inline namespace Bar{
        void ool() // Version B
        {
            std::cout << "Foo::Bar::ool" << std::endl;
        }
    }
}

int main()
{
    foo_ool(); // Works
}

If the thing you want to bookmark is a type, a simple using directive should suffice. The equivalent code for you would look like:

#include <my_first_include>

// bookmark code

#include <my_second_include>

// rest of the code
Madalene answered 25/8, 2015 at 9:12 Comment(1)
Thanks. I find it strange that the compiler will accept to overload a class with a new inlined definition silently. The Foo::ool function in the enclosing namespace becomes totally unattainable. It seems to me that this behavior should trigger at least a warning. But thanks for the bookmarking tricks. That's the closest there is to a solution for this problem.Passacaglia
S
5

You cannot unambiguously refer to the symbol defined in the enclosing namespace once the inline namespace has been seen.

In particular for you case, the qualified lookup in main is rightfully flagged as being ambiguous (as you said yourself). See the last point on cppreference:

Qualified name lookup that examines the enclosing namespace will include the names from the inline namespaces even if the same name is present in the enclosing namespace.


Yet, has other pointed out in comments, you are probably facing a problem of configuration in your toolchain invocation when you try to use std::pair.

To fix you problem, you need to make sure the compiler is invoked to compile C++11 code, which would be with the flag:

-std=c++11 or -std=c++0x depending on your version of Clang/GCC

To give further context:
The inline namespace is a C++11 feature, mainly introduced to allow for symbol versioning in libraries. A C++ standard library implementation could then define different versions of symbols in nested namespaces (with non-standard names), and depending on the requested library version when compiling, the toolchain defines one of those nested namespaces as inline. It seems you are using a c++11 version of the library (since it defines some symbols, in particular pair, in the inline namespace _1), so having symbols in an inline namespace in actually what you want.

Specular answered 25/8, 2015 at 9:10 Comment(3)
Yes I actually solved my present problem this way but I was wondering how to solve it in a more generic way. Thanks for pointing these out anyway.Passacaglia
@Passacaglia Semantically, you do not have a need to reach the enclosing symbol: if you actually have the same symbol (modulo overloading) defined several times in the same namespace (written directly, through using declarations, or through inlines namespace), the problem are the multiple declaration themselves, not how to work around them : )Specular
Yes, but if the problem lies in the fact that symbols get overloaded, why does the compiler not warn about it when it happens? After all, it does issue an error with duplicate definition within the same scope.Passacaglia
G
2

I don't think you can refer ool ambiguously when an inline namespace do have a method with same name ool .

But You can try this;

#include <iostream>

namespace Foo{

    inline namespace A {
        void ool()  // Version A
        {
            std::cout << "Foo::ool" << std::endl;
        }
    }

    namespace Bar{
        void ool() // Version B
        {
            std::cout << "Foo::Bar::ool" << std::endl;
        }
    }
}


int main()
{
    Foo::ool();  // no error
}
  1. Wrap methods in namespace Foo in a namespace A then inline namespace A.
  2. Remove inline from Bar.

Now if you make a call Foo::ool(); it will invoke inline A::ool()
Bar::ool can be invoked by Foo::Bar::ool

Global answered 25/8, 2015 at 9:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.