1

I have some classes which can be checked. The code which implements this declares a function template in a header file and specializes it in different source files:

// check.h
template <class T>
bool check(const T& object);
// class1.h
struct Class1 {int mass;};
// check_class1.cpp
#include "class1.h"
#include "check.h"
template <>
bool check(const Class1& object) {return object.mass < 100;}
// class2.h
struct Class2 {int price;};
// check_class2.cpp
#include "class2.h"
#include "check.h"
template <>
bool check(const Class2& object) {return object.price < 1000;}
// class3.h
struct Class3 {int x;};
... // 10 more classes which I can check

This code is used like this:

#include "class1.h"
#include "class2.h"
#include "class3.h"
#include "check.h"

int main()
{
    Class1 object1{50};
    Class2 object2{500};
    Class3 object3{8};
    check(object1); // OK
    check(object2); // OK
    check(object3); // a link error appears here
}

This works pretty well. When I add another class Class3 which I can check, I don't need to touch the header file, because it defines a very wide interface. If I forgot to implement the check function for Class3, the linker will remind me with an error message.

My question is: is this behavior guaranteed, or does my code work by luck? I am using Visual Studio.

If I want to specialize my function template, shouldn't I declare all my specializations in the header file?

anatolyg
  • 26,506
  • 9
  • 60
  • 134
  • How is your main getting the definition of `Class1` and 2? – Mat Nov 06 '19 at 10:26
  • I actually have my classes defined in separate source files. I'll update the code; I hope this won't make it too cluttered. – anatolyg Nov 06 '19 at 10:28
  • Technically the program is UB, it violates [ODR](https://timsong-cpp.github.io/cppwp/n3337/basic.def.odr#3). Practically, there's no way I can think of when it would compile and link successfully.. – rustyx Nov 06 '19 at 10:30
  • Why not use regular overload? – Jarod42 Nov 06 '19 at 10:34
  • Why are you using explicit template specialization of an apparently unimplemented function template instead of overloading? – AProgrammer Nov 06 '19 at 10:34
  • Specialization (`template <> bool check(const Class1& object)`) should be declared before its use. You should add `template <> bool check(const Class1& object);` in your header (`class1.h`). – Jarod42 Nov 06 '19 at 10:36
  • This is exactly what I am trying to avoid. Updating the header file for each added `check` function is tedious and leads to excessive recompilation. This is also the reason I didn't use overloading for this. BTW I'll be happy if someone adds an answer to my question. – anatolyg Nov 06 '19 at 10:38
  • Or instead of overloads, regular methods.(even if it doesn't solve your recompilation issue). – Jarod42 Nov 06 '19 at 10:40
  • @rustyx can you please elaborate how exactly it violates ODR? apparently the OP's code compiles .. – SPD Nov 06 '19 at 10:58
  • Just for the record: using overloads requires restructuring of my code but otherwise it's fine. – anatolyg Nov 06 '19 at 13:15

2 Answers2

3

I'd add those declarations to be on the safe side (well, assuming I don't overload instead for whatever reason). I don't think the law is too clear on that. For one, we have

[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.

Which, if I read correctly, means that if an explicit specialization is added to main.cpp, then it must appear before main. Because that is where an implicit instantiation may occur. The paragraph doesn't make your code flat out ill-formed NDR, because the usage and the explicit specialization appear in different TU. But it does raise concerns.

On the other hand, there is this paragraph:

[temp]

7 A function template, member function of a class template, variable template, or static data member of a class template shall be defined in every translation unit in which it is implicitly instantiated unless the corresponding specialization is explicitly instantiated in some translation unit; no diagnostic is required.

This one allows us to explicitly instantiate in separate unseen TU's. But it doesn't provide an allowance for explicit specializations. Whether or not that's intentional or an omission I cannot say.

The reason it works is likely due to how the whole thing is implemented. When the function declaration is implicitly instantiated it produces a symbol that just so happens to match the one produced by the explicit specialization. Matching symbols means a happy linker, so everything builds and runs.

But from a language-lawyer perspective, I think we can call the behavior here undefined by omission. It's undefined simply because the standard doesn't address it. So going back to my opening statement, I'd add them to be on the safe side, because at least then the placement is addressed by the standard.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
0

You have to declare each explicit specialization before their use. But you can do that in the headers declaring the types for which it is specialized.

// class2.h
struct Class2 {int price;};
template <class T>
bool check(const T& object);
template <>
bool check(const Class2& object)

(I still don't understand why using overloads is not an option).

AProgrammer
  • 51,233
  • 8
  • 91
  • 143