19

I have the following code:

typedef vector<int> Vec;
typedef vector<Vec> VecOfVec;

template<typename Vec>
Vec DoSomething(const Vec &v);

template<>
VecOfVec DoSomething<VecOfVec>(const VecOfVec &v)
{
    VecOfVec r;
    for(auto i = v.begin(); i != v.end(); i++)
        r.push_back(DoSomething(*i));
    return r;
}

template<>
Vec DoSomething<Vec>(const Vec &v) // Error here
{
    return v; // for the sake of the example
}

I get the following error:

explicit specialization of 'DoSomething<vector<int> >' after instantiation

at the marked line.
The compiler insists that it already instantiated DoSomething<vector<int> >, while it cannot, and a simple program can prove it:

typedef vector<int> Vec;
typedef vector<Vec> VecOfVec;

template<typename Vec>
Vec DoSomething(const Vec &v);

template<>
VecOfVec DoSomething<VecOfVec>(const VecOfVec &v)
{
    VecOfVec r;
    for(auto i = v.begin(); i != v.end(); i++)
        r.push_back(DoSomething(*i));
    return r;
}

Results in unresolved external.
Why is the compiler saying it already instantiated it when it cannot and even does not? and why doesn't the compiler treat it as unresolved symbol, while the linker does? I know switching the method order solves it, but I want to know why is the compiler doing it.

Daniel
  • 30,896
  • 18
  • 85
  • 139

2 Answers2

17

The code requested an implicit instantiation at DoSomething(*i). The fact that you didn't define the template in that translation unit means that it could not instantiate a specialization, hence DoSomething(*i) yields an "unresolved symbol" (linker-) error in your case. To get rid of that error, you either have to define the template in that TU, or provide an explicit instantiation directive of that template in a TU where you define the template.

The mere fact that the code requested an implicit instantiation for specialization DoSomething<vector<int> > before you explicitly provided that specialization is enough for the program to become ill-formed (without a diagnostic being required though; the compiler does a good job here which it is not required to do).

As @CharlesBailey helpfully notes, declaring the explicit specialization is perfectly sufficient; a definition of it can be given elsewhere, even outside of the using TU.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • "without a diagnostic being required": is diagnostic required in the standard at all? – Daniel Oct 14 '11 at 22:35
  • I think the part *"The mere fact that the code requested an implicit instantiation for specialization `DoSomething >` before you explicitly provided that specialization is enough for the program to become ill-formed"* is not correct, or there is something missing in it. It should rather be : *The mere fact that the code requested an implicit instantiation for specialization `DoSomething >` from a **full-specialization** before you explicitly provided that specialization is enough for the program to become ill-formed*. Correct me, if I'm wrong. :-) – Nawaz Oct 14 '11 at 22:37
  • @Nawaz: so you both say that implicit is different from explicit to a level they can't be interchanged? – Daniel Oct 14 '11 at 22:42
  • @Nawaz it does not matter whether the request is from a specialization or a non-template function. If you wrote `void foo() { DoSomething >(vector()); }` in place of his function containing that loop, that would be ill-formed likewise. – Johannes Schaub - litb Oct 14 '11 at 22:44
  • 2
    You only need to _declare_ an explicit specialization before code that would cause an implicit instantiation of the specialization. The definition can be provided elsewhere. – CB Bailey Oct 14 '11 at 22:51
  • @JohannesSchaub-litb: What you wrote is not *implicit* instantiation. Anyway, what I meant is that if the *implicit* instantiation happens from a template function, then then it would not be an error, something like this : http://ideone.com/CJKzI – Nawaz Oct 14 '11 at 22:52
  • 1
    @Nawaz What I wrote causes an implicit instantiation. You get an ill-formed (NDR) program even when you cause an implicit instantiation from within a function template: `template void f() { DoSomething >(vector()); }` before providing the explicit specialization. – Johannes Schaub - litb Oct 14 '11 at 22:56
  • @JohannesSchaub-litb: That is confusing me. If that is *implicit* instantiation, then what is that in my code? http://ideone.com/CJKzI – Nawaz Oct 14 '11 at 22:59
  • @Nawaz it would cause an implicit instantiation of `DoSomething>` at line 27 in your example if you wouldn't have provided the explicit specialization. The call in the function template is dependent, it does not know yet what specialization to call in the template. – Johannes Schaub - litb Oct 14 '11 at 23:15
  • In a different example the same error appears using (Apple) Clang, whereas g++ 5.3.0 is fine that. The mentioned declaration of the explicit specialization solves the problem. Does anybody know if g++ is more forgiving in that case? – fotinsky Mar 21 '16 at 18:36
3

Generally it just means you didn't provide a "prototype" for the template specialization. In other words you didn't give the compiler a heads up that "hey, there is going to be a specialization for this specific type of the function, so don't plug in the wrong one."

In my case I had a template specialization in a .cpp file, and got this error. Providing a "function prototype" (which is just the template specialization header followed by a semicolon, just like a regular function prototype) fixed the problem.

bobobobo
  • 64,917
  • 62
  • 258
  • 363