Resolving function names within templates created using qualified types

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 pointed out in the comment, an instance of the quux<n::A>() template causes a compiler error (in 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 expect it to work the same as with quux<B>() . This should be due to the fact that foo is considered dependent. Unfortunately, my C ++ foo is not good enough. The example compiles fine when there is no foo(int) declaration, which is also surprising to me.

Any hints, explanations and workarounds are welcome.

Update 1:

I don’t want (I can’t read) to move the declaration foo(n::A) before the definition of quux (which will avoid the error).

Update 2:

Thank you for pointing out a related issue to David, calling a template function, confused by a function with an incorrect signature declared before the template . Johannes Schaub-litb's accepted answer suggests a shell class solution that will also work in my case as a workaround. However, I am not 100% satisfied with this.

Update 3:

I solved the problem by putting the definition of foo(n::A) in the namespace n . Thanks for the helpful answers of Jesse Goode and bames53, which not only point to the relevant sections of the standard, but also provide alternative solutions. Thanks to David Rodriguez - dribeas for his explanation when I did not understand the proposed solutions properly and all the other participants.

+7
source share
2 answers

I think rule 14.6.4.2p1:

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

- For the search part that uses the unqualified name search (3.4.1) or the qualified name search (3.4.3), only function declarations from the template definition context are detected.

- For the search part using the associated namespaces (3.4.2), only function declarations found in the context of the template definition or the context of the template instance.

void foo(n::A) {} does not appear in the context of the template definition, because it comes after, and foo not in the same namespace as n::A Therefore, it must be either visible until the template is defined, or included in the same namespaces as below:

 namespace n { void foo(n::A) {} } 
+2
source

My compiler error:

 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) {} ^ 

This makes it clear what the problem is.

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

You mentioned that you cannot define void foo(n::A) before quux() , however, when you say in comments that you cannot define it in the n namespace, the reasons you give do not seem to apply. This should solve the problem without the other problems you mentioned.

 template<typename T> void quux() { foo(T()); } namespace n { void foo(n::A) {} } using n::foo; // in case there 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 cannot transfer the definition of void foo(n::A) to where it works with the correct two-phase search (again, before quux() or inside the n namespace), there is a kind of hacker solution that may work for you: forward declare the correct overload of foo() inside quux() .

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

Ultimately, the function must be defined inside the same namespace as quux() , and it must conform to the declaration of the 'generic' forward.


Or another alternative. It was quite recently that most C ++ compilers started offering the correct two-phase name lookup, so there is a lot of code that is not correct, but which compilers want to support. If you cannot change your code, this may be a good candidate for including the compatibility compiler option; my compiler accepts the -fdelayed-template-parsing flag to disable the search for two-phase names and instead always searches for names in the creation context.

+1
source

All Articles