[C++] Linker error when specializing over a parameterized type

36b416ed76cbaff49c8f6b7511458883
0
poita 101 Aug 13, 2009 at 01:12

The following code gives me a linker error whenever the specialized version is used, specifically that the specialized version is multiply defined.

This code is all inside a header.

#ifndef GLEXTENSIONS_H
#define GLEXTENSIONS_H

#include <windows.h>
#include <gl/gl.h>
#include "Vector3.h"

template <typename V>
void glVertex(const V& v)
{
    
}

template <>
void glVertex< Math::Vector3f >(const Math::Vector3f& v)
{
    glVertex3fv(&v.x);
}

#endif

// Note: Vector3f is defined (in the Math namespace):
// typedef Vector3<float> Vector3f;

Interestingly, I only get the linker error when the header is included by more than one file, but it is clearly guarded against multiple inclusions, so I don’t understand how that could be causing an issue.

I get the feeling that it has something to do with the fact that I’m specializing over a type that is parameterized itself, but I can’t seem to find any information regarding how to handle this (assuming I’m not handling it right already).

I’m using VC++05 v8.0

Thanks in advance.

7 Replies

Please log in or register to post a reply.

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Aug 13, 2009 at 01:27

The problem is that the specialization causes the compiler to generate actual code, just like an ordinary non-template function. Thus, when you include this in multiple source files you get multiple definitions of that function. In contrast, a function template that is not fully specialized generates no code until it’s instantiated.

One way to fix the problem is to mark the specialization inline; this causes it to have internal linkage and so the linker won’t complain about it being multiply defined.

The other way would be to have only a prototype of the specialization in the header file, and leave its definition in a source file - again, just like an ordinary, non-template function.
@poita

I get the feeling that it has something to do with the fact that I’m specializing over a type that is parameterized itself

I’m not quite sure what you mean - that the Vector3f type is itself an instantiation of a template? If so, there should be no need for special treatment, as it should function just like any other type.

However, if you would like to write a function template that can take a Vector of any dimension, you can do that as well, using what is called a “template template parameter”, where the argument to a template is itself a template:

template <int n, template<int> typename V>
void myFunc(const V<n>& v)
{
    // ...
}
36b416ed76cbaff49c8f6b7511458883
0
poita 101 Aug 13, 2009 at 01:30

But shouldn’t the #ifndef guard prevent it from being generated twice?

36b416ed76cbaff49c8f6b7511458883
0
poita 101 Aug 13, 2009 at 01:33

Actually ignore that. Obviously it still gets generated twice because the file is getting included in two separate translation units, correct?

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Aug 13, 2009 at 01:39

@poita

Obviously it still gets generated twice because the file is getting included in two separate translation units, correct?

Yes.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Aug 13, 2009 at 13:25

The One Definition Rule:
3.2/3

Every program shall contain exactly one definition of every non-inline function or object that is used in that
program; no diagnostic required. The definition can appear explicitly in the program, it can be found in the
standard or a user-defined library, or (when appropriate) it is implicitly defined (see 12.1, 12.4 and 12.8).
An inline function shall be defined in every translation unit in which it is used.

3.2/5

There can be more than one definition of a class type (clause 9), enumeration type (7.2), inline function
with external linkage (7.1.2), class template (clause 14), non-static function template (14.5.5), static data
member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template special-
ization for which some template parameters are not specified (14.7, 14.5.4) in a program provided that each
definition appears in a different translation unit, and provided the definitions satisfy the following require-
ments.

The specialization violates 3.2/3. The general template does not, as an exception is made for non-static function templates (but not their explicit specilializations) in 3.2/5

36b416ed76cbaff49c8f6b7511458883
0
poita 101 Aug 13, 2009 at 13:47

I’m not quite sure what you mean - that the Vector3f type is itself an instantiation of a template? If so, there should be no need for special treatment, as it should function just like any other type.

Yep, you’re absolutely right. I was just being an idiot and looking at the wrong place :)

Note that you should even specify inline for the general template definition as well. Template function definitions are just as suspect to the One Definition Rule as every other function definition. The only practical difference is that most compilers don’t complain about them.

Oh really?

Wait, so is there any (standard conforming) way of having a non-inline template function without using export, and without having to explicitly state which template instances you want instantiating? Or is the reason most compilers ignore it because of the fact that most compilers don’t implement export?

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Aug 13, 2009 at 14:25

Oh really?

No. I edited my post :). I was reading in 14/4 that “template definitions shall obey the one definition rule”, which actually surprised me, but reading up on the actual ODR definition revealed the exception to the rule :).