3

Let's say I have a base template function foo<T>(), with a full specialization for T = int. The body of the base template for foo<T>() invokes a another template, Bar<T>::baz, but foo<int>() does not.

If foo<int>() is written, the compilation fails if Bar<int> doesn't have a member baz! Why is this? We never call the base template, and can't because it's masked by the specialization! This is confirmed if the reference to Bar is removed from the base template; compilation succeeds and we see that the specialized version of foo() is indeed called as expected.

foo.h

#include <iostream>

template <typename T>
class Bar {};

template <typename T>
void foo(T arg) {
    Bar<T>::foo(); // XXX: error; comment to fix
    std::cout << "using base template" << std::endl;
}

foo.cpp

#include "foo.h"
template <> void foo<int>(int i) {
    std::cout << "specialization" << std::endl;
}

main.cpp

#include "foo.h"

int main(int argc, char **argv) {
    int i = 1;
    foo<int>(i); // prints "specialization" if noted line is commented
    return 0;
}

Why does this fail to compile?

trbabb
  • 1,894
  • 18
  • 35
  • 1
    The surprising thing is that it prints "specialization" when it does compile. The `foo` version isn't known to exist when compiling main.cpp, is it? –  Jun 03 '14 at 07:34
  • works fine in VS without commenting the line (and that should be the standard behavior). What compiler are you using? – Rakib Jun 03 '14 at 07:34
  • To begin with, you should declare the full specialization of the `foo` function template in your header file. – Constructor Jun 03 '14 at 07:34
  • Rakibul: It behaves the same way for me (error) with clang and gcc on Mavericks. – trbabb Jun 03 '14 at 07:35
  • @RakibulHasan Did your test setup have the same three files, or did you put the specialization in the header or `#include "foo.cpp"`? –  Jun 03 '14 at 07:36
  • delnan, Constructor: Common practice seems to be to place full specializations in their own .cpp files, to avoid double instantiations when the same header is in multiple translation units. See [this question](http://stackoverflow.com/questions/4445654/multiple-definition-of-template-specialization-when-using-different-objects), for example. – trbabb Jun 03 '14 at 07:36
  • 1
    @trbabb Yes, I know, but you should declare it in the header file if you want to use it in other translation units. – Constructor Jun 03 '14 at 07:38
  • @delnan, no I had different structure. And I now know why it worked. – Rakib Jun 03 '14 at 07:45
  • I think it's undefined behaviour when to instantiate a template for which an explicit specialization is defined but not visible at the point of the instantiation. – Kerrek SB Jun 03 '14 at 08:21
  • @trtabb: Your code violates the one definition rule, and does so in a way that no diagnostic is required (i.e., undefined behavior). On your computer, your code compiles and runs, printing `specialization `. On another computer, your code when compiled and run might print `using base template`. On yet another computer, the compiler/linker might complain and refuse to build an executable. All three outcomes, and more, are acceptable. Undefined behavior is the tool builder's get out of jail free card. – David Hammen Jun 03 '14 at 09:09
  • @KerrekSB What about [temp.expl.spec] 14.7.3/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."? – Constructor Jun 03 '14 at 10:35
  • @Constructor: Yep, that's the one. The lack of diagnostic makes this a particularly insidious error, but such is the nature of the C++ compilation model... – Kerrek SB Jun 03 '14 at 13:24

1 Answers1

3

You see this problem because specialization is placed in other compilation unit or other .cpp file. Place it in foo.h to get the expected behavior.

EDIT
For your question in comment about dealing with multiple definition problem, please check the example below.

You can move definition of specialization to a .cpp file and keep the declaration into header file as shown below:

foo.h

#include <iostream>

template <typename T>
class Bar {};

template <typename T>
void foo(T arg) {
    Bar<T>::foo();
    std::cout << "using base template" << std::endl;
}

template <>
void foo<int>(int i); // declaration

foo_int.cpp

#include "foo.h"

template<>
void foo<int>(int i)
{
    std::cout << "specialization" << std::endl;
}

main.cpp

#include "foo.h"

void something();

int main(int argc, char **argv) {
    int i = 1;
    foo<int>(i);
    something();
    return 0;
}

something.cpp

#include "foo.h"

void something() {
    int i = 1;
    foo<int>(i);
}
Mohit Jain
  • 30,259
  • 8
  • 73
  • 100
  • Then how do I deal with [this problem](http://stackoverflow.com/questions/4445654/multiple-definition-of-template-specialization-when-using-different-objects)? – trbabb Jun 03 '14 at 07:38
  • As noted there, declare in header/define in .cpp just like any other function. – MSalters Jun 03 '14 at 07:40
  • @MSalters: Ah, duh. I don't know how I missed that permutation. Maybe not used to doing it that way with templates. If Mohit amends his answer to clarify the separation, I'll accept it. I'm still curious what the heck is happening in my case, though. – trbabb Jun 03 '14 at 07:46