Skip to main content
GameDev.net gamedev.net
🔒 Locked

Template name lookup & overloaded functions.

Started by yacwroy Jan 15, 2010 at 10:36 PM 6 replies 1.6k views
Original Post
yacwroy
yacwroy
I'm overloading a function foo(), then calling a function bar() defined between the original definition of foo() and the overloaded definition of foo(). bar() calls foo(). Unfortunately, even though the call to bar() is after the overload of foo(), bar() chooses the first version of foo(). Yet bar() is able to see the overload of foo() (proven by removing the original definition of foo(), then successfully compiling) Placing the overload of foo() above the definition of bar() also fixes things.
template <typename pTYPE>
void foo(pTYPE arg)
  { arg.nid(); }

template <typename pTYPE>
void bar()
  {
    pTYPE var;
    foo(var);
  }

void foo(int)
  {}

int main()
  {
    int i;
    foo(i);     // OK: Resolves foo(int).
    bar<int>(); // ERROR: Fails to resolve foo(int).
  }


G++:
.cpp: In function 'void foo(pTYPE) [with pTYPE = int]':
.cpp:9:5:   instantiated from 'void bar() [with pTYPE = int]'
.cpp:19:14:   instantiated from here
.cpp:3:5: error: request for member 'nid' in 'arg', which is of non-class type 'int'


Is there something going on here that I don't understand? Does this compile OK for other people? Specs: G++ (4.5.0 20091003) Ubuntu 9.04 (x86-64) Edit: Tags & Problem.
fastcall22
fastcall22
Exactly as the error statement says; the member function nid does not exist for type int.

struct asdf {	void nid() {	}};template <typename pTYPE>void foo(pTYPE arg) {	arg.nid(); }template <typename pTYPE>void bar() {	pTYPE var;	foo(var);}void foo( asdf ) {}int main() {	asdf i;	foo(i);     // OK: Resolves foo(asdf).	bar<asdf>(); // OK: Resolves to bar(asdf), which instantiates foo(asdf), which calls asdf::nid()	bar<int>(); // ERROR: Resolves to bar(int) [ok], which instantiates foo(int) [ok], which calls int::nid() [error!]}
aryx
aryx
Is that the way you've actually written your code? If so is it as simple as declaring void foo(int) before bar()?
My bad, I shouldn't have skipped ahead of the line that stated this fixes things. I see what you're saying now, and nope, I cannot explain it so I'm hoping to be enlightened! I noticed that if I make foo(int) a specialization (i.e., placing template <> before the function) it compiles fine.

[Edited by - aryx on January 15, 2010 11:51:53 PM]
yacwroy
yacwroy
Quote:
Exactly as the error statement says; the member function nid does not exist for type int.
nid()'s just there to cause the compiler to fail if the wrong overload of foo() is chosen. Sorry, I should've mentioned that.

Quote:
Is that the way you've actually written your code? If so is it as simple as declaring void foo(int) before bar()?
It's a gross simplification, as simple as I could get while preserving the problem.

The layout isn't very easy to alter. I'm trying to preserve modularity somewhat.

The trouble is, bar() and the original version of foo() are in a header (#1).
The class with the overloaded foo() is in a second header (#2) that #includes #1.

However, #2, including the class itself, relies on #1. And #1 is all together as a module.
Lastly, many functions using #2 #include #1 before #2. I would like to keep it this way.



As I understand it (and I'm not saying I understand it correctly), the issue is the compiler is choosing the less derived of two matching functions, both visible at the time of the outermost call, because one was not visible at the time of declaration of bar(). However, this visibility of the more derived function doesn't cause problems when the less derived function is removed.

I thought that the rule was to always choose the most derived matching function that is visible from the outermost call, ie, what the compiler has parsed. This would explain why the code compiles fine when the original definition of foo() is removed.



I'm using a mildly buggy early G++ but I also tried it with the current stable 4.3.3 as there's no C++0x involved.
Shinkage
Shinkage
The code you posted works fine in MSVC '08, directly copy/pasted and MS language extensions turned off. As far as I'm aware, it SHOULD. The only reason that error should be happening, as far as I know, is if you define foo(int) AFTER main().

What it SEEMS like it's doing is resolving foo(var) while it's parsing template<> bar(), which it shouldn't do.


Unless I'm missing something I'd say this is a compiler bug?

EDIT: Can you forward-declare any types so one file doesn't have to include the other?
yacwroy
yacwroy
Quote:
Unless I'm missing something I'd say this is a compiler bug?
Thanks for that.
Would someone using G++ mind compiling the source (1st post's 1st code box, as is) so that I can check it's not just me. Thanks in advance.


Quote:
What it SEEMS like it's doing is resolving foo(var) while it's parsing template<> bar(), which it shouldn't do.

Perhaps. Deleting the original foo() fixes the trouble, though. But I guess if it does some early partial resolving where it can that could explain it.

Quote:
EDIT: Can you forward-declare any types so one file doesn't have to include the other?
Not easily.
I can declare foo(TYPE) before #including foo(T) in TYPE's header, but some source #includes foo(T) (indirectly) before #including TYPE's header, and this works well at keeping things modular. Note: there's more than one class that overloads foo() like this.

I can hack around it acceptably for now if it turns out to be a bug.
aryx
aryx
Quote:
Original post by yacwroy
Quote:
Unless I'm missing something I'd say this is a compiler bug?
Thanks for that.
Would someone using G++ mind compiling the source (1st post's 1st code box, as is) so that I can check it's not just me. Thanks in advance.


I compiled it using g++ (version 4.4.2) and received the same error, so it seems to be a g++ thing. As mentioned in the edit of my first response, if I made it so the function was specialized, things compiled fine.
Shinkage
Shinkage
Quote:
Original post by yacwroy
Not easily.
I can declare foo(TYPE) before #including foo(T) in TYPE's header, but some source #includes foo(T) (indirectly) before #including TYPE's header, and this works well at keeping things modular. Note: there's more than one class that overloads foo() like this.

I can hack around it acceptably for now if it turns out to be a bug.


Often when I get into a complex tangle of forward declarations I'll just create a new header file that's NOTHING BUT forward declarations for everything and include that in everything that doesn't need a type to be fully resolved and only including the real thing where it's absolutely necessary that the type be resolved. It's a lot easier than messing around with remembering what's forward-declared where.

EDIT2: This is the reason I'd be inclined to call this a compiler bug. The foo candidate should be getting thrown out during substitution. In MSVC when you get an error because all substitutions are invalid it's something like, "No X function takes Y parameter," or whatever the exact wording is IIRC.

EDIT: Or you could hack around it and fully specialize bar() for int like so:

template <typename pTYPE>void foo(pTYPE arg)  { arg.nid(); }template <typename pTYPE>void bar()  {    pTYPE var;    foo(var);  }void foo(int)  {}// Full specialization of bar for inttemplate<>void bar<int>(){    int var;    foo(var);}int main()  {    int i;    foo(i);    bar<int>();  }


It's always a pain to have to use compiler hacks like that, but would that fix the problem?

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.