Overload resolution for operator functions - c++

Why does this code not compile ? I would have expected that the built-in unary operator& would have been selected, such that the ambiguity between the two binary operator& should not matter. If you comment out one of the binary operator&, the code compiles (with gcc 11.2) and the built-in unary operator& is selected.
struct A
{ int operator&(int) const { return 1; }
};
struct B
{ int operator&(int) const { return 2; }
};
struct C : A, B
{};
C* test(C& c)
{ return &c; } //error: request for member 'operator&' is ambiguous

Problem is overload resolution. First function name should be matched and if abutting is found error should be reported. After that matching of arguments is performed. Here is an example with regular function: https://godbolt.org/z/nYfjdn1Td
As you can see ambiguity is reported for foo on all compilers even although both foos have different number of arguments.
Same issue should pop up for operator& so more like gcc is right here.
Overload resolution is a complex topic. Usually you do not have to think about it. Here is nice source which explains how complex this is.
If you wish to fix problem and have binary operator available for such code, you can do this:
struct C : A, B
{
using A::operator&;
using B::operator&;
};
This fixes problem with your test on all compilers.
of course ambiguity for instaceOfC & intValue remains, but you can resolve it by doping one using statement.
https://godbolt.org/z/ds3W4GYcT

This is due to unqualified name lookup.
Initially, name lookup begins in the scope of class C. No declarations of operator& are found.
Then look up proceeds to base classes in turn. In each of A and B there is a different declaration with the name operator&.
At this point the set of declarations found by name lookup is A::operator& and B::operator&. These are considered ambiguous at the name lookup stage so overload resolution, where the difference between unary and binary operator& would be discovered, is never reached.
See the example at cppreference for unqualified name look up for more info.
If only one of A or B was inherited then the name lookup set wouldn't be member function name lookup set wouldn't be ambiguous. It would proceed to overload resolution an correctly deduce unary operator&.
Adding a using declaration to the body of class C, bring them both into the scope C:: for name lookup..
struct C : A, B
{
using A::operator&;
using B::operator&;
};
The scope of C is searched before base classes so the ambiguous step is never reached.

Related

Is it allowed to overload operator+ for a standard library type and a built-in type?

Is it allowed to overload operators, such as operator+ for a combination of a standard library type and a built-in type, when such an overload doesn't exist?
For example, is it legal to implement the following operator in the default namespace or a user-defined namespace:
std::string operator+(const std::string& s, int right) { ... }
I know there are various restrictions about implementing things in the std:: namespace, but it isn't clear to me if there is any rule against the above (whether it's a good idea is of course an entirely different matter!).
For example, is it legal to implement the following operator in the default namespace or a user-defined namespace:
std::string operator+(const std::string& s, int right) { ... }
Yes, it's perfectly legal to do this. The only restrictions are about adding names to namespace std or specializing member function function templates or class templates or adding deduction guides for class templates in std.
There's nothing that stops you from writing something like:
namespace N {
std::string operator+(std::string s, int ) { return s; }
}
That is a well-formed program, per the standard. However note that since by definition your operators will not have any program-defined types in them, they will never be found by ADL. Since they are operators, which typically are found by ADL, this in of itself may be a reason to avoid such a pattern:
namespace U {
auto foo() {
return "hello"s + 1; // error: name lookup doesn't find our operator
}
auto bar() {
using namespace N;
return "hello"s + 1; // ok: for some definition of ok
}
}
It is legal to write that. But it won't necessarily do what you want.
If your goal is to make some_string + 4 legal C++ code, then its legality will depend on exactly where that expression appears. Operator overloading will use ADL to look up the operator [over.match.oper]/3.2:
The set of non-member candidates is the result of the unqualified lookup of operator# in the context of the expression according to the usual rules for name lookup in unqualified function calls (6.4.2) except that all member functions are ignored. However, if no operand has a class type, only those non-member
functions in the lookup set that have a first parameter of type T1 or “reference to cv T1 ”, when T1 is an enumeration type, or (if there is a right operand) a second parameter of type T2 or “reference to cv T2”, when T2 is an enumeration type, are candidate functions.
But since your operator+ is not in the same namespace as either argument, ADL lookup will fail. And the list of non-member candidates will not automatically include global functions, there's no chance for it to find the right operator+.
So you cannot effectively overload an operator like this without opening up the namespace. Which of course is forbidden for std.
It's perfectly OK to overload operator+ between std::string and int. The only restriction that the standard places on that is (https://timsong-cpp.github.io/cppwp/n3337/over.oper#6):
An operator function shall either be a non-static member function or be a non-member function and have at least one parameter whose type is a class, a reference to a class, an enumeration, or a reference to an enumeration.
There is no restriction on the being able to define them in your namespace for even the types from the standard library.

Overload resolution for multiply inherited operator()

First, consider this C++ code:
#include <stdio.h>
struct foo_int {
void print(int x) {
printf("int %d\n", x);
}
};
struct foo_str {
void print(const char* x) {
printf("str %s\n", x);
}
};
struct foo : foo_int, foo_str {
//using foo_int::print;
//using foo_str::print;
};
int main() {
foo f;
f.print(123);
f.print("abc");
}
As expected according to the Standard, this fails to compile, because print is considered separately in each base class for the purpose of overload resolution, and thus the calls are ambiguous. This is the case on Clang (4.0), gcc (6.3) and MSVC (17.0) - see godbolt results here.
Now consider the following snippet, the sole difference of which is that we use operator() instead of print:
#include <stdio.h>
struct foo_int {
void operator() (int x) {
printf("int %d\n", x);
}
};
struct foo_str {
void operator() (const char* x) {
printf("str %s\n", x);
}
};
struct foo : foo_int, foo_str {
//using foo_int::operator();
//using foo_str::operator();
};
int main() {
foo f;
f(123);
f("abc");
}
I would expect the results to be identical to the previous case, but it is not the case - while gcc still complains, Clang and MSVC can compile this fine!
Question #1: who is correct in this case? I expect it to be gcc, but the fact that two other unrelated compilers give a consistently different result here makes me wonder whether I'm missing something in the Standard, and things are different for operators when they're not invoked using function syntax.
Also note that if you only uncomment one of the using declarations, but not the other, then all three compilers will fail to compile, because they will only consider the function brought in by using during overload resolution, and thus one of the calls will fail due to type mismatch. Remember this; we'll get back to it later.
Now consider the following code:
#include <stdio.h>
auto print_int = [](int x) {
printf("int %d\n", x);
};
typedef decltype(print_int) foo_int;
auto print_str = [](const char* x) {
printf("str %s\n", x);
};
typedef decltype(print_str) foo_str;
struct foo : foo_int, foo_str {
//using foo_int::operator();
//using foo_str::operator();
foo(): foo_int(print_int), foo_str(print_str) {}
};
int main() {
foo f;
f(123);
f("foo");
}
Again, same as before, except now we don't define operator() explicitly, but instead get it from a lambda type. Again, you'd expect the results to be consistent with the previous snippet; and this is true for the case where both using declarations are commented out, or if both are uncommented. But if you only comment out one but not the other, things are suddenly different again: now only MSVC complains as I would expect it to, while Clang and gcc both think it's fine - and use both inherited members for overload resolution, despite only one being brought in by using!
Question #2: who is correct in this case? Again, I'd expect it to be MSVC, but then why do both Clang and gcc disagree? And, more importantly, why this is different from the previous snippet? I would expect the lambda type to behave exactly the same as a manually defined type with overloaded operator()...
Barry got #1 right. Your #2 hit a corner case: captureless nongeneric lambdas have an implicit conversion to function pointer, which got used in the mismatch case. That is, given
struct foo : foo_int, foo_str {
using foo_int::operator();
//using foo_str::operator();
foo(): foo_int(print_int), foo_str(print_str) {}
} f;
using fptr_str = void(*)(const char*);
f("hello") is equivalent to f.operator fptr_str()("hello"), converting the foo to an pointer-to-function and calling that. If you compile at -O0 you can actually see the call to the conversion function in the assembly before it gets optimized away. Put an init-capture in print_str, and you'll see an error since the implicit conversion goes away.
For more, see [over.call.object].
The rule for name lookup in base classes of a class C only happens if C itself doesn't directly contain the name is [class.member.lookup]/6:
The following steps define the result of merging lookup set S(f,Bi)
into the intermediate S(f,C):
If each of the subobject members of S(f,Bi) is a base class subobject of at least one of the subobject members of S(f,C), or if S(f,Bi) is empty, S(f,C) is unchanged and the merge is complete. Conversely, if each of the subobject members of S(f,C) is a base class subobject of at least one of the subobject members of S(f,Bi), or if S(f,C) is empty, the new S(f,C) is a copy of S(f,Bi).
Otherwise, if the declaration sets of S(f,Bi) and S(f,C) differ, the merge is ambiguous: the new S(f,C) is a lookup set with an invalid declaration set and the union of the subobject sets. In subsequent merges, an invalid declaration set is considered different from any other.
Otherwise, the new S(f,C) is a lookup set with the shared set of declarations and the union of the subobject sets.
If we have two base classes, that each declare the same name, that the derived class does not bring in with a using-declaration, lookup of that name in the derived class would run afoul of that second bullet point and the lookup should fail. All of your examples are basically the same in this regard.
Question #1: who is correct in this case?
gcc is correct. The only difference between print and operator() is the name that we're looking up.
Question #2: who is correct in this case?
This is the same question as #1 - except we have lambdas (which give you unnamed class types with overload operator()) instead of explicit class types. The code should be ill-formed for the same reason. At least for gcc, this is bug 58820.
Your analysis of the first code is incorrect. There is no overload resolution.
The name lookup process occurs wholly before overload resolution. Name lookup determines which scope an id-expression is resolved to.
If a unique scope is found via the name look-up rules, then overload resolution begins: all instances of that name within that scope form the overload set.
But in your code, name lookup fails. The name is not declared in foo, so base classes are searched. If the name is found in more than one immediate base class then the program is ill-formed and the error message describes it as an ambiguous name.
The name lookup rules do not have special cases for overloaded operators. You should find that the code:
f.operator()(123);
fails for the same reason as f.print failed. However, there is another issue in your second code. f(123) is NOT defined as always meaning f.operator()(123);. In fact the definition in C++14 is in [over.call]:
operator() shall be a non-static member function with an arbitrary number of parameters. It can have default arguments. It implements the function call syntax
postfix-expression ( expression-list opt )
where the postfix-expression evaluates to a class object and the possibly empty expression-list matches the parameter list of an operator() member function of the class. Thus, a call x(arg1,...) is interpreted as x.operator()(arg1, ...) for a class object x of type T if T::operator()(T1, T2, T3) exists and if the operator is selected as the best match function by the overload resolution mechanism (13.3.3).
This actually seems an imprecise specification to me so I can understand different compilers coming out with different results. What is T1,T2,T3? Does it mean the types of the arguments? (I suspect not). What are T1,T2,T3 when multiple operator() function exist, only taking one argument?
And what is meant by "if T::operator() exists" anyway? It could perhaps mean any of the following:
operator() is declared in T.
Unqualified lookup of operator() in the scope of T succeeds and performing overload resolution on that lookup set with the given arguments succeeds.
Qualified lookup of T::operator() in the calling context succeeds and performing overload resolution on that lookup set with the given arguments succeeds.
Something else?
To proceed from here (for me anyway) I would like to understand why the standard didn't simply say that f(123) means f.operator()(123);, the former being ill-formed if and only if the latter is ill-formed. The motivation behind the actual wording might reveal the intent and therefore which compiler's behaviour matches the intent.

operator[] lookup into template base classes

The following code is causing a little headache for us: clang and MSVC accepts the following code, while GCC rejects it. We believe GCC is right this time, but I wanted to make it sure before filing the bugreports. So, are there any special rules for operator[] lookup that I'm unaware of?
struct X{};
struct Y{};
template<typename T>
struct B
{
void f(X) { }
void operator[](X){}
};
template<typename T>
struct C
{
void f(Y) { }
void operator[](Y){}
};
template<typename T> struct D : B<T>, C<T> {};
int main()
{
D<float> d;
//d.f(X()); //This is erroneous in all compilers
d[Y()];//this is accepted by clang and MSVC
}
So is the above code is correct in resolving the operator[] call in the main function?
It's not 100% clear in which compiler the issue lie. The standard goes over a lot of rules for name lookup (which is what this is an issue with), but more specifically section 13.5.5 covers the operator[] overload:
13.5.5 Subscripting [over.sub]
1 - operator[] shall be a non-static member function with exactly one parameter. It implements the subscripting syntax
postfix-expression [ expr-or-braced-init-list ]
Thus, a subscripting expression x[y] is interpreted as x.operator[](y) for a class object x of type T if T::operator[](T1) exists and if the operator is selected as the best match function by the overload resolution mechanism (13.3.3).
Looking at the standard on Overloading (chapter 13):
13 Overloading [over]
1 - When two or more different declarations are specified for a single name in the same scope, that name is said to be overloaded. By extension, two declarations in the same scope that declare the same name but with different types are called overloaded declarations. Only function and function template declarations can be overloaded; variable and type declarations cannot be overloaded.
2 - When an overloaded function name is used in a call, which overloaded function declaration is being referenced is determined by comparing the types of the arguments at the point of use with the types of the parameters in the overloaded declarations that are visible at the point of use. This function selection process is called overload resolution and is defined in 13.3.
...
13.2 Declaration matching [over.dcl]
1 - Two function declarations of the same name refer to the same function if they are in the same scope and have equivalent parameter declarations (13.1). A function member of a derived class is not in the same scope as a function member of the same name in a base class.
So according to this and section 10.2 on derived classes, since you've declared struct D : B, C, both B and C have member functions for operator[] but different types, thus the operator[] function is overloaded within the scope of D (since there's no using nor is operator[] overridden or hidden directly in D).
Based on this, MSVC and Clang are incorrect in their implementations since d[Y()] should be evaluated to d.operator[](Y()), which would produce an ambiguous name resolution; so the question is why do they accept the syntax of d[Y()] at all?
The only other areas I could see with regards to the subscript ([]) syntax make reference to section 5.2.1 (which states what a subscript expression is) and 13.5.5 (stated above), which means that those compilers are using other rules to further compile the d[Y()] expression.
If we look at name lookup, we see that 3.4.1 Unqualified name lookup paragraph 3 states that
The lookup for an unqualified name used as the postfix-expression of a function call is described in 3.4.2.
Where 3.4.2 states:
3.4.2 Argument-dependent name lookup [basic.lookup.argdep]
1 - When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function or function template declarations (11.3) not otherwise visible may be found.
2 - For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are determined in the following way:
...
(2.2) - If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members. [ Note: Non-type template arguments do not contribute to the set of associated namespaces.—end note ]
Note the emphasis on may.
With the above points and a couple of others from 3.4 (name lookup), one could believe that Clang and MSVC are using these rules to find d[] first (and thus finding it as C::operator[]) vs. using 13.5.5 to turn d[] into d.operator[] and continuing compilation.
It should be noted that bringing the operators of the base classes into scope of the D class or using explicit scope does, however, 'fix' this issue across all three compilers (as is expected based on the using declaration clauses in the references), example:
struct X{};
struct Y{};
template<typename T>
struct B
{
void f(X) { }
void operator[](X) {}
};
template<typename T>
struct C
{
void f(Y) { }
void operator[](Y) {}
};
template<typename T>
struct D : B<T>, C<T>
{
using B<T>::operator[];
using C<T>::operator[];
};
int main()
{
D<float> d;
d.B<float>::operator[](X()); // OK
//d.B<float>::operator[](Y()); // Error
//d.C<float>::operator[](X()); // Error
d.C<float>::operator[](Y()); // OK
d[Y()]; // calls C<T>::operator[](Y)
return 0;
}
Since the standard is ultimately left to the interpretation of the implementer, I'm not sure which compiler would be technically correct in this instance since MSVC and Clang might be using other rules to compile this though, given the subscripting paragraphs from the standard, I'm inclined to say they are not strictly adhering to the standard as much as GCC is in this instance.
I hope this can add some insight into the problem.
I believe that Clang and MSVC are incorrect, and GCC is correct to reject this code. This is an example of the principle that names in different scopes do not overload with each other. I submitted this to Clang as llvm bug 26850, we'll see if they agree.
There is nothing special about operator[] vs f(). From [over.sub]:
operator[] shall be a non-static member function with exactly one parameter. [...] Thus, a subscripting expression x[y] is interpreted as x.operator[](y) for a class object x of type T
if T::operator[](T1) exists and if the operator is selected as the best match function by the overload
resolution mechanism
So the rules governing the lookup of d[Y()] are the same as the rules governing d.f(X()). All the compilers were correct to reject the latter, and should have also rejected the former. Moreover, both Clang and MSVC reject
d.operator[](Y());
where both they accept:
d[Y()];
despite the two having identical meaning. There is no non-member operator[], and this is not a function call so there is no argument-dependent lookup either.
What follows is an explanation of why the call should be viewed as ambiguous, despite one of the two inherited member functions seeming like it's a better match.
The rules for member name lookup are defined in [class.member.lookup]. This is already a little difficult to parse, plus it refers to C as the object we're looking up in (which in OP is named D, whereas C is a subobject). We have this notion of lookup set:
The lookup set for f in C, called S(f,C), consists of two component sets: the declaration set, a set of
members named f; and the subobject set, a set of subobjects where declarations of these members (possibly
including using-declarations) were found. In the declaration set, using-declarations are replaced by the set
of designated members that are not hidden or overridden by members of the derived class (7.3.3), and type
declarations (including injected-class-names) are replaced by the types they designate.
The declaration set for operator[] in D<float> is empty: there is neither an explicit declaration nor a using-declaration.
Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f,C) is
initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi,
and merge each such lookup set S(f,Bi) in turn into S(f,C).
So we look into B<float> and C<float>.
The following steps define the result of merging lookup set S(f,Bi)
into the intermediate S(f,C):
— If each of the subobject members of S(f,Bi) is a base class subobject of at least one of the subobject
members of S(f,C), or if S(f,Bi) is empty, S(f,C) is unchanged and the merge is complete. Conversely,
if each of the subobject members of S(f,C) is a base class subobject of at least one of the
subobject members of S(f,Bi), or if S(f,C) is empty, the new S(f,C) is a copy of S(f,Bi).
— Otherwise, if the declaration sets of S(f,Bi) and S(f,C) differ, the merge is ambiguous: the new
S(f,C) is a lookup set with an invalid declaration set and the union of the subobject sets. In subsequent
merges, an invalid declaration set is considered different from any other.
— Otherwise, the new S(f,C) is a lookup set with the shared set of declarations and the union of the
subobject sets.
The result of name lookup for f in C is the declaration set of S(f,C). If it is an invalid set, the program is
ill-formed. [ Example:
struct A { int x; }; // S(x,A) = { { A::x }, { A } }
struct B { float x; }; // S(x,B) = { { B::x }, { B } }
struct C: public A, public B { }; // S(x,C) = { invalid, { A in C, B in C } }
struct D: public virtual C { }; // S(x,D) = S(x,C)
struct E: public virtual C { char x; }; // S(x,E) = { { E::x }, { E } }
struct F: public D, public E { }; // S(x,F) = S(x,E)
int main() {
F f;
f.x = 0; // OK, lookup finds E::x
}
S(x, F) is unambiguous because the A and B base subobjects of D are also base subobjects of E, so S(x,D)
is discarded in the first merge step. —end example ]
So here's what happens. First, we try to merge the empty declaration set of operator[] in D<float> with that of B<float>. This gives us the set {operator[](X)}.
Next, we merge that with the declaration set of operator[] in C<float>. This latter declaration set is {operator[](Y)}. These merge sets differ, so the merge is ambiguous. Note that overload resolution is not considered here. We are simply looking up the name.
The fix, by the way, is to add using-declarations to D<T> such that there is no merge step done:
template<typename T> struct D : B<T>, C<T> {
using B<T>::operator[];
using C<T>::operator[];
};

friend function

What does the following mean (especially the highlighted portion)? Why is the expression 'f(a)' treated as a 'cast expression'?
C++03 $3.4./3- "The lookup for an unqualified name
used as the postfix-expression of a
function call is described in 3.4.2.
[Note: for purposes of determining
(during parsing) whether an expression
is a postfix-expression for a function
call, the usual name lookup rules
apply. The rules in 3.4.2 have no
effect on the syntactic interpretation
of an expression. For example,
typedef int f;
struct A {
friend void f(A &);
operator int();
void g(A a) {
f(a);
}
};
The expression f(a) is a cast-expression equivalent to int(a). Because the expression is not a function call, the argument-dependent name lookup (3.4.2) does not apply and the friend function f is not found. ]"
Any thoughts?
It means that the parser first determines whether the expression before the parentheses is an id or a postfix-expression. In this case, it sees f, and the nearest f defined is typedef int f - therefore it concludes the expression is interpreted as int(a) and no Koenig lookup is performed.
Let's have this code (you an try it online: http://ideone.com/clone/eRKvP)
typedef int f;
namespace x {
struct A {
friend void f(A &);
//friend void f(); // # 1
operator int();
void g(A a) {
//void f(); // # 2
(void)f(a); // # 3
}
};
}
Had you declared an (unrelated) function f visible from g, it would be interpreted as a function call and Koenig lookup would find the correct overload (see line #2).
The friend declaration of f should not apply here, because
11.4/1: ... The name of a friend is not in the scope of the class,
However, what's disturbing me, is that uncommenting #1 makes the compiler (gcc in this case) call x::f(A&) too. Not sure why is that. [In Comeau Online compiler, it works as expected (eg. #1 doesn't influence line #3). The latest beta version has problems compiling this code, though.]
PS: As noted by litb, the rules in C++0x are a little different:
3.4.2/3:
Let X be the lookup set produced by
unqualified lookup (3.4.1) and let Y be
the lookup set produced by argument
dependent lookup (defined as follows).
If X contains
a declaration of a class member, or
a block-scope function declaration that is not a using-declaration, or
a declaration that is neither a function or a function template
then Y is empty. Otherwise Y is the
set of declarations found in the
namespaces associated with the
The bold item will make the compiler only consider void f() and fail for too many arguments.
It seems to have been introduced by the resolution of issue 218.

Ambiguous call to templated function due to ADL

I've been bitten by this problem a couple of times and so have my colleagues. When compiling
#include <deque>
#include <boost/algorithm/string/find.hpp>
#include <boost/operators.hpp>
template< class Rng, class T >
typename boost::range_iterator<Rng>::type find( Rng& rng, T const& t ) {
return std::find( boost::begin(rng), boost::end(rng), t );
}
struct STest {
bool operator==(STest const& test) const { return true; }
};
struct STest2 : boost::equality_comparable<STest2> {
bool operator==(STest2 const& test) const { return true; }
};
void main() {
std::deque<STest> deq;
find( deq, STest() ); // works
find( deq, STest2() ); // C2668: 'find' : ambiguous call to overloaded function
}
...the VS9 compiler fails when compiling the second find. This is due to the fact that STest2 inherits from a type that is defined in boost namespace which triggers the compiler to try ADL which finds boost::algorithm::find(RangeT& Input, const FinderT& Finder).
An obvious solution is to prefix the call to find(…) with "::" but why is this necessary? There is a perfectly valid match in the global namespace, so why invoke Argument-Dependent Lookup? Can anybody explain the rationale here?
ADL isn't a fallback mechanism to use when "normal" overload resolution fails, functions found by ADL are just as viable as functions found by normal lookup.
If ADL was a fallback solution then you might easily fall into the trap were a function was used even when there was another function that was a better match but only visible via ADL. This would seem especially strange in the case of (for example) operator overloads. You wouldn't want two objects to be compared via an operator== for types that they could be implicitly converted to when there exists a perfectly good operator== in the appropriate namespace.
I'll add the obvious answer myself because I just did some research on this problem:
C++03 3.4.2
§2 For each argument type T in the function call, there is a set of zero or more associated namespaces [...] The sets of namespaces and classes are determined in the following way:
[...]
— If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a
member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces
in which its associated classes are defined.
§ 2a If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated
namespaces and classes are not considered. Otherwise the set of declarations found by the lookup of
the function name is the union of the set of declarations found using ordinary unqualified lookup and the set
of declarations found in the namespaces and classes associated with the argument types.
At least it's standard conformant, but I still don't understand the rationale here.
Consider a mystream which inherits from std::ostream. You would like that your type would support all the << operators that are defined for std::ostream normally in the std namespace. So base classes are associated classes for ADL.
I think this also follows from the substitution principle - and functions in a class' namespace are considered part of its interface (see Herb Sutter's "What's in a class?"). So an interface that works on the base class should remain working on a derived class.
You can also work around this by disabling ADL:
(find)( deq, STest2() );
I think you stated the problem yourself:
in the global namespace
Functions in the global namespace are considered last. It's the most outer scope by definition. Any function with the same name (not necessarily applicable) that is found in a closer scope (from the call point of view) will be picked up first.
template <typename Rng, typename T>
typename Rng::iterator find( Rng& rng, T const& t );
namespace foo
{
bool find(std::vector<int> const& v, int);
void method()
{
std::deque<std::string> deque;
auto it = find(deque, "bar");
}
}
Here (unless vector or deque include algorithm, which is allowed), the only method that will be picked up during name look-up will be:
bool foo::find(std::vector<int> const&, int);
If algorithm is somehow included, there will also be:
template <typename FwdIt>
FwdIt std::find(FwdIt begin, FwdIt end,
typename std::iterator_traits<FwdIt>::value_type const& value);
And of course, overload resolution will fail stating that there is no match.
Note that name-lookup is extremely dumb: neither arity nor argument type are considered!
Therefore, there are only two kinds of free-functions that you should use in C++:
Those which are part of the interface of a class, declared in the same namespace, picked up by ADL
Those which are not, and that you should explicitly qualified to avoid issues of this type
If you fall out of these rules, it might work, or not, depending on what's included, and that's very awkward.