Template Specialization Question

I have a function declared as such in a header file:

1
2
3
4
5
6
template <class T>
std::string to_string(T thing) {
	std::stringstream ret;
	ret<<thing;
	return ret.str();
}


This compiles fine, however, when I try to add a specialization below it:

1
2
3
4
template <> //just for practice really
std::string to_string(std::string thing) {
	return thing;
}


I receive a bunch of linker errors stating that the specialized version of the function is already defined in the other object files. What exactly is the problem? Do I need to put the specialization somewhere else?
You cannot make a specialization of a template function.

EDIT: Apparently I was wrong. The code you posted compiles and links fine on mingw.

EDIT 2: One thing that comes to my mind is that this specialization is not "visible" everywhere where the base template is. In this situation one compilation unit could use std::string to_string(std::string thing) generated based on the base template, and another compilation unit could use std::string to_string(std::string thing) generated from the specialization, what would lead to link errors.

EDIT 3: Tested again with two compilation units, and mingw generates errors even if visibility of the base tamplate and its specialization is the same. It looks that the compiler treats the specialization just like a regular function, i.e. its symbol name is global. Adding static to the function's declaration helped.
Last edited on
Are you using Microsoft Visual Studio??
There are no specializations of function templates. But you can have several function templates with the same function name. The only difference between instantiated templates and normal overloads is that 1) templates have lower priority when it comes to overload resolution and do not contend with non-template functions (therefore not causing ambiguity errors with them) 2) template functions are instantiated on a per-need basis.

So, I would understand ambiguity error from the compiler, but linker error? May be the problem is that you instantiate in one translation unit the parametrized template with string and in another the non-parametrized template and the mangled names of the resulting compiled functions are the same and the linker dies. That would explain why adding static modifier fixes the problem. But I still can not understand why you don't get ambiguity error for overload resolution during compilation. May be I don't understand overload resolution that well.

Regards
Last edited on
samples:
mytemplate.h
1
2
3
4
5
#ifndef MYTEMPLATE_H
#define MYTEMPLATE_H
template <class T> T max(T t1, T t2);
#include "myTemplate.cpp"
#endif // MYTEMPLATE_H 

myTemplate.cpp
1
2
3
4
5
6
7
8
9
10
11
12
#include "myTemplate.h"
#include <iostream>
#define V std::string
template <class T> T max(T t1, T t2)
{
    return (t1 > t2) ? t1 : t2;
}

template <> V max<V>(V t1, V t2)
{
    return (t1 > t2) ? t1 : t2;
}


tmain.cpp
1
2
3
4
5
6
7
8
9
10
#include "myTemplate.h"
#include <iostream>

int main(void)
{

    std::cout << max<std::string>(std::string("right"), std::string("wrong")) << std::endl;
    std::cin.get();
    return 0;
}
True, this works. I was confused because partial template specialization is not available. But I didn't know that full template specialization works ok with functions. I need to look into that stuff more seriously :)
Anyway the linker error is because the "specialized" version needs to be (declared) inline.
All template specializations need to be inline? You mean, like, always?

And is there a difference between
template <> std::string to_string(std::string thing) and
template <> std::string to_string<std::string>(std::string thing)?

Are they both template specializations of the to_string template, or only the latter one and the former one is separate template?
Hi simeonz,
The latter one is good declaration for specializations template.
Yes, that was my guess also - the second one is specialization like in your example. But the one from the snippet in the original post is not specialization, but separate template, or am I wrong?
Putting the implementation of a concrete (non-template) function in a header file is the same thing as putting a global variable in a header file, only the duplicate symbol definition linker error complains about the name of the function instead of the name of the variable.
jsmith wrote:
Putting the implementation of a concrete (non-template) function in a header file is the same thing as putting a global variable in a header file, only the duplicate symbol definition linker error complains about the name of the function instead of the name of the variable.



To confirm what JSmith said - the following is from the book C++ Templates - The Complete Guide concerning function templates.

A full specialization is in many ways similar to a normal declaration (or rather, a normal redeclaration). In particular, it does not declare a template, and therefore only one definition of a noninline full function template specialization should appear in a program.
Thanks! Following up with the "specializations are basically normal functions" info, I did this:

Header:
1
2
3
4
5
6
7
8
9
template <class T>
std::string to_string(T thing) {
	std::stringstream ret;
	ret<<thing;
	return ret.str();
}

template <>
std::string to_string(std::string thing);


And moved the definition to the .cpp and it compiled fine.
That's very useful info. But does this mean that full template specialization participates in overload resolution with the same priority as non-template function overloads?

And I still don't understand what is the difference between the syntaxes
template <> std::string to_string(std::string thing) and
template <> std::string to_string<std::string>(std::string thing)?
Are they the same thing? You certainly can not use the former syntax in circumstances like these:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

template <int N> int f()
{
  return N;
}

template <> int f<5>()
{
  return 6;
}

int main(void)
{

    std::cout << f<5>() << std::endl;
}
They are the almost the same. The first allows the compiler to infer the template type from the arguments (as normal) and the second type lets you tell the compiler explicitly. In your example, the compiler can't guess the template arguments from the function, so you need to specify.

EDIT: Misinterpreted something.
Last edited on
Oh, ok. So it is the same mechanism as in template instantiation. You either specify the template arguments explicitly or rely on their deduction. Boy, was I confused on the subject.

To answer my last question. Fully specialized function templates are apparently assigned lower priority to non-template functions in overload resolution:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <typeinfo>

template <typename T> void f(T)
{
  std::cout << "template: " << typeid(T).name() << std::endl;
}

template <> void f(int)
{
  std::cout << "spec. template: int" << std::endl;
}

void f(int)
{
  std::cout << "non-template: int" << std::endl;
}

int main(void)
{
  f(int()); //prints "non-template: int"
}
@simeonz:

Here's a link that might help you out with your question: http://www.gotw.ca/publications/mill17.htm
Be careful about what compiler you are using for your test. For me, different versions of gcc give
different results for the code examples given in the link.
Overload resolution of base templates. This is first time I encounter that. Thanks a lot.
Topic archived. No new replies allowed.