Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ linking and template specializations

I'm studying the behavior of the C++ linker with respect to template specializations. I'm using Microsoft Visual C++ 2010 for these experiments. I don't know if the behavior is the same with other toolchains (e.g. gcc).

Here's a first code snippet:

// bar.cpp

template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }

// main.cpp

template <typename T> int foo() { return 1; }
template <> int foo<double>() { return 2; }

int bar();

int main()
{
    const int x = bar();
    const int y = foo<double>();  // doesn't link
}

Expectedly, this code doesn't link because foo<double>() has multiple definitions as it gets instantiated once in bar.cpp and once in main.cpp (via specialization). We would then expect, if this program would link, that bar() and main() would use distinct instantiations of foo() such that at the end we would have x == 1 and y == 2.

Let's fix the link error by declaring the specialization of foo<double>() as static:

// bar.cpp

template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }

// main.cpp

template <typename T> int foo() { return 1; }
template <> static int foo<double>() { return 2; }  // note: static

int bar();

int main()
{
    const int x = bar();          // x == 1
    const int y = foo<double>();  // y == 2
}

We now have x == 1 and y == 2, as we expected. (Note: we must use the static keyword here: an anonymous namespace won't do since we can't specialize a template function in a different namespace than its declaration.)

Now, the use of the static keyword is rather unintuitive. Typically, the specialization foo<double>() would reside somewhere in a header file, and thus would be marked as inline, like in the following snippet:

// bar.cpp

template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }

// main.cpp

template <typename T> int foo() { return 1; }
template <> inline int foo<double>() { return 2; }  // note: inline

int bar();

int main()
{
    const int x = bar();          // x == 2
    const int y = foo<double>();  // y == 2
}

This code now links properly and when we run it we get x == 2 and y == 2. This is the bit I find surprising: why is there a single definition of foo<double>()? What is the meaning of inline in this code?

A last snippet:

// bar.cpp

template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }

// main.cpp

template <typename T> int foo() { return 1; }
template <> inline int foo<double>() { return 2; }  // note: inline

int bar();

int main()
{
    const int x = bar();             // x == 1
    // const int y = foo<double>();  // note: commented out
}

This case is actually not surprising: the specialization of foo<double>() is no longer instantiated in main.cpp (although the declaration is still there), so the only instantiation remaining is the one in bar.cpp.

like image 324
François Beaune Avatar asked Aug 25 '11 12:08

François Beaune


People also ask

What is a template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

What is the specialty of a template function give example?

Template in C++is a feature. We write code once and use it for any data type including user defined data types. For example, sort() can be written and used to sort any data type items. A class stack can be created that can be used as a stack of any data type.

Why do we need template specialization?

Overloading it by either a different function template or a non-template function is arguably superior because its handling is more intuitive and it's overall more powerful (effectively by overloading the template, you have a partial specialization of the template, even though technically it's called partial ordering).

What is the difference between generic class template and specialization template?

Key differences between generics and C++ templates: Generics are generic until the types are substituted for them at runtime. Templates are specialized at compile time so they are not still parameterized types at runtime. The common language runtime specifically supports generics in MSIL.


1 Answers

You are actually violating a C++ rule here (emphasis mine):

14.7.3 [temp.expl.spec]:

6/ If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined. [ Example:

class String { };

template<class T> class Array { /* ... */ };
template<class T> void sort(Array<T>& v) { /* ... */ }

void f(Array<String>& v) {
  sort(v); // use primary template
  // sort(Array<T>&), T is String
}

template<> void sort<String>(Array<String>& v); // error: specialization
                                                // after use of primary template

template<> void sort<>(Array<char*>& v); // OK: sort<char*> not yet used

template<class T> struct A {
  enum E : T;
  enum class S : T;
};

template<> enum A<int>::E : int { eint }; // OK
template<> enum class A<int>::S : int { sint }; // OK
template<class T> enum A<T>::E : T { eT };
template<class T> enum class A<T>::S : T { sT };
template<> enum A<char>::E : int { echar }; // ill-formed,
                                            // A<char>::E was instantiated
                                            // when A<char> was instantiated
template<> enum class A<char>::S : int { schar }; // OK

end example ]

like image 108
Matthieu M. Avatar answered Oct 21 '22 15:10

Matthieu M.