Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Necessity of forward-declaring template functions

I recently created this example code to illustrate C++11 variadic template function usage.

template <typename Head, typename... Tail> void foo (Head, Tail...);
template <typename... Tail> void foo (int, Tail...);
void foo () {}

template <typename... Tail>
void foo (int x, Tail... tail)
{
    std :: cout << "int:" << x;
    foo (tail...);
}

template <typename Head, typename... Tail>
void foo (Head x, Tail... tail)
{
    std :: cout << " ?:" << x;
    foo (tail...);
}

foo (int (123), float (123)); // Prints "int:123 ?:123.0"

If the first two lines which forward-declare foo are omitted then this prints int:123int:123 instead. This surprised a certain experienced and knowledgeable C++ programmer.

He was convinced the forward-declarations shouldn't be necessary because the body won't be instantiated until the second phase of two-phase lookup. He thinks the compiler (gcc 4.6) has a bug.

I believe the compiler is right because the two foo are different base template functions and the choice of base template needs to be locked-in during the first phase or else you could violate the one-definition rule by instantiating foo before all versions of it have been defined and then again afterwards (consider how the linker assumes that redundant template function definitions are identical, interchangeable, and discardable).

So, who is right?


The above-linked GOTW nicely explains how and why function templates don't partially specialise, but the existence of variadic template functions seems to add to the confusion -- the intuition that foo<int,Tail...> should be a partial specialisation of foo<Head,Tail...> is stronger than that intuition for non-variadic functions, at least to me.

like image 983
spraff Avatar asked Aug 31 '11 09:08

spraff


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.

What is the purpose of a forward declaration of a class?

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.

Can you forward declare a template?

You can declare default arguments for a template only for the first declaration of the template. If you want allow users to forward declare a class template, you should provide a forwarding header. If you want to forward declare someone else's class template using defaults, you are out of luck! Save this answer.

What is the importance of function templates?

Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type.


2 Answers

GCC (and Clang) are right. MSVC would get it wrong because it does not implement the look-up correctly.

There is, it seems, a misunderstanding from your colleague. The rules for look-up are:

  • the Base template function need be declared before it is called from a definition
  • the Specialized template function need be declared before it is instantiated

Note: those rules apply for free-functions, within a class no forward declaration is required

Note that because a definition also acts as a declaration it is unnecessary, in your example, to forward declare the int version.

Correct example:

template <typename T> void foo(T);             // declare foo<T>

template <typename T> void bar(T t) { foo(t); }// call foo<T> (dependent context)

template <> void foo<int>(int);                // declare specialiaztion foo<int>

void bar(int i) { foo(i); }                    // instantiate foo<T> with int
                                               // which is the specialization

If there is base template available, this is an error. If the specialization is not declared prior to the instantiation, it won't be used, and this may, subsequently, mean a violation of the ODR rule (if another instantiation uses the specialization).

From the Standard (C++0x FDIS):

14.6.4.2

1. 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.

Note that the paragraphs referred to are for regular functions.

like image 124
Matthieu M. Avatar answered Sep 24 '22 01:09

Matthieu M.


Two-phase lookup will find:

  • functions which are visible at the point of definition, and
  • functions which can be found by ADL at the point of instantiation.

template <typename Head, typename... Tail> void foo (Head x, Tail... tail) can't be found by ADL, so if isn't visible at the point of definition it won't be found at all.

In other words, GCC is right.

like image 32
ymett Avatar answered Sep 27 '22 01:09

ymett