Operator in namespace scope hiding another in global scope
Asked Answered
A

3

13

Is this a compiler-bug?

template <typename T>
T& operator++(T& t)
{
    return t;
}

namespace asdf {

enum Foo { };
enum Bar { };

Foo& operator++(Foo& foo);

void fun()
{
    Bar bar;
    ++bar;
}

} // end namespace asdf

int main()
{
    return 0;
}

The GCC 4.7 error message is:

error: no match for 'operator++' in '++bar'
note: candidate is:
note: asdf::Foo& asdf::operator++(asdf::Foo&)
note: no known conversion for argument 1 from 'asdf::Bar' to 'asdf::Foo&'

It compiles if you comment out the line:

Foo& operator++(Foo& foo);
Adara answered 16/1, 2013 at 8:38 Comment(3)
Yes it is....Transship
Don't think so. VC++ generates the same.Olethea
@KarthikT: I am not sure how your linked code supports the "is a bug"-argument.W
T
14

No that is not a bug. There are three parallel sets of operators considered. Members, non-member operators, and builtins.

The non-member ones are looked up by normal unqualified+ADL lookup, ignoring all class member functions. Hence the global operator is hidden by a lexical more closer one (and an intervening member function wouldn't have hidden other non-members).

Note that overload resolution takes place after name lookup1; in your case the name operator++ was found, but no appropriate overload.

If Bar had been declared globally, and/or the other operator in namespace asdf, ADL (in the former case) or ordinary unqualified lookup (in the latter case) would have dragged the operator in.


1: Overload resolution (...) takes place after name lookup has succeeded. (C++ Standard)

Trophic answered 16/1, 2013 at 8:58 Comment(4)
I am not sure if this is enough detail. In case of a non-match, shouldn't the next enclosing namespace be looked up?W
@phresnel: But how does the name operator++ form a non-match for operator++? It is only the name that is relevant during name lookup.Clardy
@BartvanIngenSchenau: I was provoking litb to add this detail (trying to think through the questioners perspective, if you want); as is, I don't think I can upvote already.W
@phresnel feel free to improve the answer by editing it. hugsTrophic
C
8

No, this is not a compiler bug.

There are two name-lookups that get performed for the expression ++bar.

  • The regular name lookup searches the enclosing scopes and namespaces until it finds the first occurence of operator++. This search works inside out, so the global namespace is searched last. When looking for operator functions, member-functions are treated separately (and don't stop this search).
  • The argument-dependent lookup kicks in next and searches additional classes and namespaces, but only those that are related to the arguments of the function (operator++ in this case).

In the example in the question, the normal lookup finds asdf::operator++ and stops looking.
The argument-dependent lookup only adds the asdf namespace to the places to search, because that is the associated namespace for enum Bar. For that reason, the global operator++ can not be found.

You can make the global operator++ be found with a using declaration in namespace asdf.

Clardy answered 16/1, 2013 at 9:3 Comment(5)
the first bullet you describe does not consider class scopes. that is, even if in a class member function you use an operator expression, a namespace scope operator, even in a nonadl related namespace scope, can be found despite there being a member operator with the same name.Trophic
@JohannesSchaub-litb: Is that a change between C++03 and C++11? Because I can't find such an exception in C++03 clause 3.4.1 [basic.lookup.unqual].Clardy
the rules are specified in 13.3.1.2 :-)Trophic
I'm porting code between compilers. Compiler A accepts the code in this question (it apparently includes the global operator for overload resolution as well) - should that be considered a compiler bug?Pocked
@MattMcNabb: Yes, if compiler A accepts the code in this question, when invoked in its conforming mode, then compiler A has a bug.Clardy
C
1

Overloading only applies to names defined in the same scope. Once the compiler finds a matching name it doesn't look in outer scopes, even if the name it found applies to something that can't be used. This has nothing to do with operators; if the code used a function name in the same way that it uses operator++ it would get the same error. For example:

void f(int);

struct C {
void f(const C&);
void g() {
    f(3); // error: f(const C&) can't be called with argument 3
};
Citified answered 16/1, 2013 at 12:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.