Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why no need of forward declaration in static dispatching via templates?

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.

like image 223
vsoftco Avatar asked Aug 07 '14 17:08

vsoftco


People also ask

Why do we need forward declaration?

A forward declaration allows us to tell the compiler about the existence of an identifier before actually defining the identifier. In the case of functions, this allows us to tell the compiler about the existence of a function before we define the function's body.

When can you use forward declaration?

You will usually want to use forward declaration in a classes header file when you want to use the other type (class) as a member of the class. You can not use the forward-declared classes methods in the header file because C++ does not know the definition of that class at that point yet.

What is forward declaration and where we can use this?

A forward declaration tells the compiler about the existence of an entity before actually defining the entity. Forward declarations can also be used with other entity in C++, such as functions, variables and user-defined types.

What is forwarding declaration?

In computer programming, a forward declaration is a declaration of an identifier (denoting an entity such as a type, a variable, a constant, or a function) for which the programmer has not yet given a complete definition.


1 Answers

f_helper(typename T::tag_type{}) is a type-dependent expression because T::tag_type is a dependent type. This means that f_helper doesn't need to be visible until f<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:

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.

If the function name is an unqualified-id and the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.

The last paragraph to me indicates this is undefined behaviour. The function call that depends on a template parameter here is f_helper(typename T::tag_type{}). f_helper isn't visible when f is instantiated, but it would be if we performed name lookup after all translation units have been compiled.

like image 141
Simple Avatar answered Sep 21 '22 05:09

Simple