I am playing a bit with static polymorphism, I'm calling a function which internally calls the "right" specialized function depending on the type of the initial argument (basically I'm doing tagging). Here is the code:
#include <iostream>
using namespace std;
// tags
struct tag1{};
struct tag2{};
// the compliant types, all should typedef tag_type
struct my_type1
{
using tag_type = tag1;
};
struct my_type2
{
using tag_type = tag2;
};
// static dispatch via tagging
template <typename T>
void f(T)
{
cout << "In void f<typename T>(T)" << endl;
// why can I call f_helper without forward definition?!?
f_helper(typename T::tag_type{});
}
int main()
{
my_type1 type1;
my_type2 type2;
// how does f below knows about f_helper ?!?!
// even after instantiation f_helper shouldn't be visible!
f(type1);
f(type2);
}
// helper functions
void f_helper(tag1)
{
cout << "f called with my_type1" << endl;
}
void f_helper(tag2)
{
cout << "f called with my_type2" << endl;
}
So, f(T)
is called with a parameter my_type1
or my_type2
that internally must typedef tag_type
with the appropriate tag tag1
/tag2
. Depending on this internal tag_type
, the "right" wrapper is then called, and this decision is made of course at compile time. Now I really don't understand why this code IS working? Why don't we need to forward-declare f_helper
? I first had the wrappers defined before main
(and after f
), and I though ok, this makes sense, you don't need to forward declare because the compiler instantiate the template only when f(type1);
is called (in main()
), before it doesn't know the type T
, so at the time of instantiation the compiler knows f_wrapper
.
But as you see, even if I declare the wrappers AFTER main()
, the code still works. Why is this happening? I guess the question is a bit strange, asking why a code works :)
EDIT
The code continues to compile even in gcc5 and gcc HEAD 6.0.0.
f_helper(typename T::tag_type{})
is a type-dependent expression becauseT::tag_type
is a dependent type. This means thatf_helper
doesn't need to be visible untilf<T>
is instantiated due to two phase lookup.EDIT: I'm pretty sure that this is actually undefined behaviour. If we look at 14.6.4.2 [temp.dep.candidate] we see this passage:
The last paragraph to me indicates this is undefined behaviour. The
function call that depends on a template parameter
here isf_helper(typename T::tag_type{})
.f_helper
isn't visible whenf
is instantiated, but it would be if we performed name lookup after all translation units have been compiled.