14

Take following code as an example

#include <algorithm>

namespace baz {
    template<class T>
    void sort(T&&){}
}

namespace boot {
    const auto sort = [](auto &&){};
}

void foo (){
    using namespace std;
    using namespace baz;
    sort(1);
}

void bar(){
    using namespace std;
    using namespace boot;
    sort(1);
}

I expected that since foo compiled, then bar shall compile as well. To my surprise, the foo compiles correctly and bar has problem with ambiguous call to sort function. Am I doing something illegal here or this is proper way compiler should behave? If so, why is it so different. I though generic lambda can be treated as syntactic sugar for generic function.

live example

bartop
  • 9,971
  • 1
  • 23
  • 54
  • 5
    Lambdas do not participate in ADL – Guillaume Racicot Mar 26 '19 at 22:50
  • 10
    This isn't ADL. An `int` argument doesn't come from any namespace. – chris Mar 26 '19 at 22:53
  • 4
    Should this really be ambiguous, though? `std::sort()` doesn't take 1 parameter as input, it takes at least 2, so why is the compiler even considering it as a candidate for a call that passes only 1 parameter value? – Remy Lebeau Mar 26 '19 at 23:15
  • There must be something about the extra layer of indirection that the lambda introduces. With the first example, the call is made to `::baz::sort`, but in the second example, it would have to find `::boot::mystery_lambda_type::operator()`. That extra step might be what causes `std::sort` to be considered first. I don't have the standard in front of me so can't be sure about this. – alter_igel Mar 26 '19 at 23:22

1 Answers1

16

The problem here is not that the call to sort is ambiguous, but that the name sort is ambiguous. Name lookup happens before overload resolution.

I believe the relevant section is [basic.lookup]/1, specifically

[…] The declarations found by name lookup shall either all denote the same entity or shall all denote functions or function templates; in the latter case, the declarations are said to form a set of overloaded functions ([over.load]). […]

In your case, the name sort denotes both, the object boot::sort as well as the set of overloaded functions std::sort. Therefore, name lookup fails.

Your code is really no different from if you had written, for example

namespace baz {
    int a;
}

namespace boot {
    int a;
}

void foo() {
    using namespace baz;
    using namespace boot;
    a = 42;  // error: reference to 'a' is ambiguous
}

Try it out here; compare this to a case that actually has an ambiguous function call; note how the error message is the same as in your case, specifically referring to the name itself being ambiguous rather than the function call.

Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
  • 1
    I think this is actually the right answer. And I would like to add that if both the template function `sort` and the lambda `sort` were declared in the same namespace, it would be an error. You cannot have a function and non-function with the same name in the same namespace. So there could never be an overload set that has both true functions and function-like objects. – Mike Mar 27 '19 at 01:09
  • Seems like a right answet to me. If there is something that can be done to workaround my problem I would be thankful for comment/answer edit. – bartop Mar 27 '19 at 07:05
  • 1
    @Scheff indeed, I must've mixed up the URLs somehow. Should be fixed now. Thanks for pointing that out! – Michael Kenzel Mar 27 '19 at 10:08
  • @bartop can you not just remove the using directives and/or use fully-qualified names, e.g., `baz::sort`!? – Michael Kenzel Mar 27 '19 at 11:31
  • @MichaelKenzel Sure can, and I do it currently. Even though I still wonder if there is different way to avoid this ambiguity – bartop Mar 27 '19 at 11:49
  • @bartop I don't think there possibly could be a way to avoid this. As the passage quoted above says: You must never have a situation where the same name refers to more than one entity unless it's the name of an overload set. The only way to avoid the ambiguity is to not make the name ambiguous. In your case, that would mean either renaming one of the two things or not introducing the name of both things into the same scope. – Michael Kenzel Mar 27 '19 at 12:48