7

Consider the following C++ code example:

namespace n
{
    struct A {};
}

struct B {};

void foo(int) {}

template<typename T>
void quux()
{
    foo(T());
}

void foo(n::A) {}
void foo(B) {}


int main()
{
    quux<n::A>(); // Error (but works if you comment out the foo(int) declaration)
    quux<B>();    // Works

    return 0;
}

As indicated in the comment, the template instantiation quux<n::A>() causes a compiler error (on GCC 4.6.3):

foo.cpp: In function ‘void quux() [with T = n::A]’:
foo.cpp:22:16:   instantiated from here
foo.cpp:13:5: error: cannot convert ‘n::A’ to ‘int’ for argument ‘1’ to ‘void foo(int)’

Can someone explain to me what is going on? I would have expected for it to work the same as with quux<B>(). It must have something to do with when foo is considered dependent. Unfortunately my C++ foo is not good enough. The example compiles fine, when the foo(int) declaration is not present, which is also surprising to me.

Any hints, explanations and workarounds are welcome.

Update 1:

I do not want to (read cannot) move the declaration of foo(n::A) before the definition of quux (which would avoid the error).

Update 2:

Thanks for David for pointing out the related question Template function call confused by function with wrong signature declared before template. The accepted answer by Johannes Schaub - litb proposes a wrapper class solution, that would also work in my case as a workaround. However, I'm not 100% happy with it.

Update 3:

I solved the issue by putting the definition of foo(n::A) in namespace n. Thanks for the helpful answers of Jesse Good and bames53 that not only point out the relevant sections of the standard, but also provide alternative solutions. Thanks to David Rodríguez - dribeas for his explanation when I did not understand the proposed solutions properly and all other contributors.

Community
  • 1
  • 1
Nikolaus Demmel
  • 379
  • 1
  • 3
  • 11
  • Move the `quux` *body* to a later position in the code, *after* all the names `foo` have been declared. – Kerrek SB Nov 09 '12 at 22:07
  • Yes, that solves the error. However in my real program that is not possible/desired. – Nikolaus Demmel Nov 09 '12 at 22:10
  • 1
    @KerrekSB: That might or not be an option. Clang++ yields a more informative message: *error: call to function 'foo' that is neither visible in the template definition nor found by argument-dependent lookup*. Now you will need to find where the lookup for that particular expression is defined in the standard :) --possible workaround: move `foo(n::A)` to the `n` namespace – David Rodríguez - dribeas Nov 09 '12 at 22:17
  • 1
    possible duplicate of [Template function call confused by function with wrong signature declared before template](http://stackoverflow.com/questions/13223278/template-function-call-confused-by-function-with-wrong-signature-declared-before) – David G Nov 09 '12 at 22:17
  • you cannot even have forward declarations of foo before the definition of quux? – didierc Nov 09 '12 at 22:18
  • @didirec: No. Think of `quux()` as `write(std::vector)` and `foo(n::A)` as `write(n::A)`. I want the vector version in a common header file, to use in the print definitions of other print functions all around the place. – Nikolaus Demmel Nov 09 '12 at 22:25
  • What about move the body of `quux()` after your `main()` function and keep its signature in the same place? – Yamaneko Nov 09 '12 at 22:27
  • @David: Not sure whether it is a duplicate yet. But definitely strongly related. It seems I do not quite understand dependent name lookup right. I'm trying to read up in the standard sections pointed out in that related post and Jesse Good's answer below. – Nikolaus Demmel Nov 09 '12 at 22:36
  • @VictorHugo: Not a viable solution, thanks. Think of `quux` as being defined in a header file. – Nikolaus Demmel Nov 09 '12 at 22:38
  • @DavidRodríguez-dribeas: Thanks, I need to find out exactly how argument dependent lookup works. Moving `foo` into the namespace `n` is not an option. Moreover, it does not solve the problem, but rather results in `quux` not working either and also removing the `foo(int)` will then also not solve the issue but rather result in an error and template definition (because now the `n::foo` name is not dependent, because it is a qualified name). – Nikolaus Demmel Nov 09 '12 at 22:44
  • 1
    @NikolausDemmel: I am not sure you understood the proposed change: move `foo(n::A)` into the `n` namespace (leave `foo(B)` where it is, that is in the same namespace as `B`). Do not make a qualified call to `foo` (i.e. leave `foo(T())`, do not make it `n::foo(T())`). That should fix it (assuming that you can move `foo(n::A)` into `namespace n`) – David Rodríguez - dribeas Nov 10 '12 at 01:57
  • @DavidRodríguez-dribeas: Aha, you are right. I misinterpreted your proposal. In fact this is very workable in my case and solves the problem. Thank you! If you want to add it as an answer, I will accept that answer. Otherwise I will accept Jesse Good's answer (as he explains why it does not work) and point out your solution in an update to my question. – Nikolaus Demmel Nov 10 '12 at 03:22
  • 1
    @NikolausDemmel: Jesse's answer actually explains what the issue is and also hints at the solution with the snippet where `void foo(A)` is moved to the `n` namespace. You should accept his answer. – David Rodríguez - dribeas Nov 10 '12 at 04:20
  • @DavidRodríguez-dribeas: You are right again. I'm too tired. Anyway, thanks everyone. – Nikolaus Demmel Nov 10 '12 at 05:44

2 Answers2

2

I think the rule is 14.6.4.2p1:

For a function call that depends on a template parameter, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2, 3.4.3) except that:

— For the part of the lookup using unqualified name lookup (3.4.1) or qualified name lookup (3.4.3), only function declarations from the template definition context are found.

— For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.

void foo(n::A) {} is not visible in the template definition context because it comes after and foo is not in the same namespace as n::A. So it needs to be either visible before the template definition or included in the same namespace like below:

namespace n
{
    void foo(n::A) {}
}
Community
  • 1
  • 1
Jesse Good
  • 50,901
  • 14
  • 124
  • 166
  • Thanks for the hint and references to the standard. I think I'm getting close, however I still don't quite understand. The fact the `quux()` works is because `foo` is in the same namespace as `B`, thus `foo` is found in the associated namespace (global in the example) during lookup for instantiation context? `quux()` does not work, because `foo` is not found in the associated namespace (which is only `n`)? Why does it work if `foo(int)` is commented out? – Nikolaus Demmel Nov 09 '12 at 23:26
  • I just found a solution. If I call `::quux(T)`, then it works. My explanation would be that the explicit qualification makes the global namespace an associated namespace. Does that make sense? Is there a solution that could be part of the definition of quux, not the use of quux? **Update:** That was bogus, it doesn't actually work. – Nikolaus Demmel Nov 09 '12 at 23:28
  • On top of the question of why it works if `foo(int)` is commented out, I'd like to find out if there is a way/workaround that adds the namespace of the definition of `quux` (i.e. `::`) or the point of instantiation of `quux` (also `::`) to the associated namespaces that are search in instantiation context? – Nikolaus Demmel Nov 09 '12 at 23:46
  • And one last thing. This associated namespace lookup (3.4.2) is the Koenig lookup, right? – Nikolaus Demmel Nov 09 '12 at 23:47
  • 1
    It shouldn't work if `foo(int)` is commented out ([see here](http://liveworkspace.org/code/2bb1dbe3f1427ae3b9d638919dbfb710)). Another name for argument dependent lookup (ADL) is Koenig lookup after the person who proposed it to the C++ standards committee. – Jesse Good Nov 11 '12 at 21:26
1

The error my compiler gives is:

main.cpp:11:5: error: call to function 'foo' that is neither visible in the template definition nor found by argument-dependent lookup
    foo(T());
    ^
main.cpp:18:5: note: in instantiation of function template specialization 'quux<n::A>' requested here
    quux<n::A>(); // Error (but works if you comment out the foo(int) declaration)
    ^
main.cpp:14:6: note: 'foo' should be declared prior to the call site or in namespace 'n'
void foo(n::A) {}
     ^

Which makes it clear what the problem is.

Commenting out void foo(int) does not make it work however; that's just a bug/extension in your compiler.

You mention that you can't define void foo(n::A) before quux(), however when you say in the comments that you can't define it inside namespace n, the reasons you give don't seem to apply. This should fix the problem without the other problems you mention.

template<typename T>
void quux()
{
    foo(T());
}

namespace n {
    void foo(n::A) {}
}
using n::foo; // in case there's any other code that depends on getting foo(n::A) from the global namespace

void foo(B) {} // this stays in the global namespace

If you can't move the definition of void foo(n::A) to where it works with proper two-phase lookup (again, either before quux() or inside namespace n) there's a sort of hacky solution which may work for you: forward declare the proper overload of foo() inside quux().

template<typename T>
void quux()
{
    void foo(T);
    foo(T());
}

The function eventually must be defined inside the same namespace as quux() and it has to match the 'generic' forward declaration.


Or there's another alternative. It's only been fairly recent that most C++ compilers started offering correct two-phase name lookup, so there's a lot of code out there that isn't correct but which compilers want to support. If you can't change your code then it may be a good candidate for enabling a compatibility compiler option; my compiler takes the flag -fdelayed-template-parsing to disable two-phase name lookup and instead always look names up in the instantiation context.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • Thanks for the very detailed answer. Your first solution is actually what I have now implemented (thanks to David Rodríguez - dribeas and Jesse Good). When I said I could not put `foo(n::A)` in the `n` namespace I did not understand the solution yet. Thanks also for the two alternative solutions, despite me not needing them in this instance. – Nikolaus Demmel Nov 12 '12 at 21:48
  • What compiler are you using? The error message is clearly a lot superior to gcc. – Nikolaus Demmel Nov 12 '12 at 21:48