c++namespacesusing-declaration

Ambiguity while working with namespaces: Can `using` single thing leak anything else?


I have problem with ambiguity while working with namespaces.

Code:

namespace app::hal {
    enum class Motor {
        Left  = 0,
        Right = 1
    };

    void setMotor(Motor which, float duty);
}

namespace app::control {
    using Motor = app::hal::Motor;

    void setMotor(Motor which, float duty);
    float getMotor(Motor which);

}

inline void test() {
    using app::control::setMotor;
    using app::control::Motor;
    setMotor(Motor::Left, 0);
}

Error:

<source>:24:17: error: call of overloaded 'setMotor(app::hal::Motor, int)' is ambiguous
   24 |         setMotor(Motor::Left, 0);
      |         ~~~~~~~~^~~~~~~~~~~~~~~~
<source>:15:14: note: candidate: 'void app::control::setMotor(Motor, float)'
   15 |         void setMotor(Motor which, float duty);
      |              ^~~~~~~~
<source>:8:14: note: candidate: 'void app::hal::setMotor(Motor, float)'
    8 |         void setMotor(Motor which, float duty);
      |              ^~~~~~~~

Godbolt link: https://godbolt.org/z/sqr4qKbd8

What is going on here? Why is setMotor call finding app::hal::setMotor as possibility when I didn't using namespace app::hal nor using app::hal::setMotor? Isn't this "leaking" namespace?

I don't want to change app::hal contents, and I wanted so have same naming scheme for app::control. How can I make it work?


Solution

  • What is going on here?

    This is due to ADL. Basically, here the namespace app::hal will be automatically searched and so there are two setMotor(one due to ADL and other because you explicitly specified using app::control::setMotor) and hence the ambiguity.


    How can I make it work? I don't want to change app::hal contents

    Method 1

    You can disable(workaround) ADL by adding parenthesis around setMotor when calling the function as shown below:

    inline void test() {
        using app::control::setMotor;
        using app::control::Motor;
    //--v--------v---------------------->adding parenthesis make this work because NO MORE ADL with parenthesis
        (setMotor)(Motor::Left, 0);   //works now with parenthesis 
    }
    

    Working demo


    Method 2

    Or you can be explicit about the call as shown below:

    inline void test() {
        namespace appcon = app::control;
        appcon::setMotor(appcon::Motor::Left, 0); //works now
    }