4

The following code is not accepted by GCC 4.6:

void F(int x,char y)
{
}
template<typename T>
void G(T t)
{
    F(t);
}
void F(int x)
{
}

int main()
{
    G(5);
    return 0;
}

Should it be?

If not, does anyone have a good idea for a work-around? The real world scenario where this occurs is where G is part of a library for solving a particular kind of problem, needing a user-supplied helper function called F. However, for different kinds of problems, F takes different number of parameters. A few sample implementations of F are shipped with the library.

What is happening is that depending on the #include-order used by the client, only the "wrong kind" of F may be visible at template declaration time, and GCC then gives up, without waiting until the user-provided, correct, F is defined. This is even though template instantiation happens after the correct F is defined.

Update: Yes I know it works if all declarations of F happen before G, or if all declarations of F happen after G. However, that doesn't really help me very much.

Update: In the code this minimal example is adapted from, F is really called 'read'. And the first declaration of read has nothing at all to do with the second. The first declaration is in one header file, and the second in another. I don't want to introduce 'strange' rules regarding include-file order, especially when the versions of 'read' have nothing to do with one another.

avl_sweden
  • 613
  • 4
  • 14
  • 1
    Regarding the update: Why not require that users pass `F` as a parameter, e.g. a function object or function pointer, to `G`? This way you force them to have it ready when they call `G` and you avoid problems with include order. – jrok Nov 04 '12 at 22:07
  • Thank you! That's a rather appealing workaround. – avl_sweden Nov 04 '12 at 22:17

4 Answers4

4

At instantiation, only argument dependent lookup is done. You can fix your case by using an argument whose type resides in the namespace of your F

void F(int x,char y)
{
}
template<typename T>
void G(T t)
{
    F(t);
}
void F(int x)
{
}

template<typename T>
struct wrapper {
 operator T() const { return t; }
 T t;
};

template<typename T> wrapper<T> make_wrapper(T t) {
  wrapper<T> w = { t };
  return w;
}

int main()
{
    G(make_wrapper(5));
    return 0;
}
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Ah, you obviously know what you're talking about. Thank you very much for the answer! I apparently need to read up a bit on "argument dependent lookup" - it seems it does'nt mean what I thought it meant! :-) – avl_sweden Nov 04 '12 at 22:23
  • But why does it work if the definition of void F(int x,char y) is moved after G? – avl_sweden Nov 04 '12 at 22:24
  • 2
    @avl_sweden that's a compiler bug. it should not work then either, if you keep calling the template with `int`. – Johannes Schaub - litb Nov 04 '12 at 22:35
1

One possible workaround is to ensure that the declaration of void F(int x) is available before that of template<typename T> void G(T t);

void F(int x);

template<typename T> void G(T t) { .... }

In your example, F(int) dependent name, so is looked up during the second phase of the two-phase lookup. However, the rules of the look-up. specified in §14.6.4 of draft n3337, specify that the name must be visible at the point of definition of the template, or in a namespace associated with the types of the function arguments (argument dependent look-up):

In resolving dependent names, names from the following sources are considered:

— Declarations that are visible at the point of definition of the template.

— Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.

So an alternative work-around to is to bring the function into the namespace of T.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • Interesting, this would explain my problem. But how can F(int) be considered non-dependent? After all, the only reason F(int) should be called is because T is int? Isn't the required function in the body of G something with signature void F(T t), and isn't that really dependent on T? – avl_sweden Nov 04 '12 at 22:15
  • @avl_sweden yes, the call `F(t)` is dependent. I don't know what "F(int) is deemed a non-dependent name" means. – Johannes Schaub - litb Nov 04 '12 at 22:20
  • But then, why didn't my example work? The lookup of F would be dependent on the type of T? And isn't lookup of functions dependent on a template parameter supposed to happen at template instantiation time? – avl_sweden Nov 04 '12 at 22:29
  • @avl_sweden Johannes is right, `F(t)` is dependent. However, the look-up rules specify that the name be available at the point of template definition, *or* through ADL. This had confused me. I edited my answer. – juanchopanza Nov 05 '12 at 09:40
0

Try placing the F(int) overload above G.

void F(int x) {}

template <typename T> void G(T t) { F(t); } // works

Functions aren't hoisted; you must either prototype or define them before they are used.

David G
  • 94,763
  • 41
  • 167
  • 253
  • Thank you for the answer. However, the function F is supposed to be implemented by the user of this library, for whatever datatype T they're using the template for. So it's possible but unfortunate to demand they declare F before the template (it would mean having to declare F before the #include of the library). Is there some other way to solve this? – avl_sweden Nov 04 '12 at 21:59
  • Define the function in another header file and include it in your .cpp file. Shouldn't that work? – David G Nov 04 '12 at 22:02
  • Yes it works, but I'm writing the function G, and my users will be calling it. If possible, I don't want to require them to #include-files in specific orders. – avl_sweden Nov 04 '12 at 22:06
  • Also, note that if the declaration of F before G is removed, the code DOES work. Function lookup in C++ template functions is supposed to be done at instantiation time, for functions dependent upon the type on which the template is specialized. You're supposed to be able to define a new type T and then use your type in std::vector, despite T not being available when std::vector was defined, for instance. – avl_sweden Nov 04 '12 at 22:07
  • Okay, sorry to spam your answer, but I have yet another question: What do you mean by saying functions aren't "hoisted"? Isn't function hoisting a term from compiler optimization? But surely my question has more to do with earlier stages of parsing, like name-lookup, than it has to do with code generation and optimization? – avl_sweden Nov 04 '12 at 22:21
  • @avl_sweden Sorry if I confused you. I used terminology from my past experiences with JavaScript. It's an interpreted language. Functions, being first-class objects, are hoisted so that they may be used before being explicitly implemented. – David G Nov 04 '12 at 22:46
0

If not, does anyone have a good idea for a work-around?

At the point where you define that template, the only F() that is known to the compiler is void F(int x,char y), which obviously doesn't match the usage in G(T t).

The solution is simple: Define or declare void F(int x) before defining template<typename T> void G(T t).

David Hammen
  • 32,454
  • 9
  • 60
  • 108
  • Thank you for your answer. However, G is a function implemented by a library, and F is a helper function that the user needs to implement for each datatype he/she wishes to use G. For instance, if G was std::vector, F could have been a function "serialize" to write a variable of a specific type to a file. It would then make sense for std::vector to implement serialize, to serialize all elements of the vector. This would require a call to serialize from within std::vector, and serialize would have to be declared _after_ the declaration of std::vector. – avl_sweden Nov 04 '12 at 22:10
  • And the above does work. However, if any variant of serialize taking different datatypes, is declared before vector is defined, you run into the problem in my question. – avl_sweden Nov 04 '12 at 22:10