1

I recently got to know that the following code is ill-formed, NDR:

// foo.h
template <typename T>
void foo();

// foo_bar.cpp
template <>
void foo<bar>()
{ /* Implementation for bar */ }

// foo_baz.cpp
template <>
void foo<baz>()
{ /* Implementation for baz */ }

Due to the specializations not being declared in foo.h. The reason for not declaring them in foo.h is to avoid #include'ing the definitions of baz and bar (long build times).

Explicit instantiations do not suffer from this problem - there's no need to declare them in the header. Therefore I thought about solving the above problem using explicit instantiation instead:

// foo.h
template <typename T>
void foo();

// foo_bar.cpp
template <typename T>
void foo()
{ /* Implementation for bar */ }

template void foo<bar>();

// foo_baz.cpp
template <typename T>
void foo()
{ /* Implementation for baz */ }

template void foo<baz>();

In some way, it would be replacing specializations with explicit instantiations. But then comes my question: is having two different implementations of the primary template foo<T> violating ODR, assuming they are instantiated for different types?

user1011113
  • 1,114
  • 8
  • 27
  • 4
    I think you can forward declare `bar` and `baz` in `foo.h`, declare your specializations there as well, then define the specializations in the cpp files. – NathanOliver Aug 25 '22 at 12:20
  • Yes, it is! Corrected, thanks! – user1011113 Aug 25 '22 at 12:25
  • 1
    Yes, simply `struct bar; template <> void foo();` in the header should be enough to make this completely well-formed. The original code is likely to work anyway. The only practical reason I could see for it to fail would be if names of explicit specializations were mangled differently, which I don't think they are in any ABI that I am aware of. Defining the template twice with different bodies feels like a worse ODR violation. – user17732522 Aug 25 '22 at 12:31
  • @HolyBlackCat Yes, they are intended to be used in other TUs. When you say "this" should be added to the code, do you mean the template specialization declarations? – user1011113 Aug 26 '22 at 07:43
  • @user1011113 I meant the use in a different TU, at least to the first snippet. I've misread the second one, which looks ill-formed NDR as is, so I've deleted my comment to not confuse anyone. – HolyBlackCat Aug 26 '22 at 08:07
  • @user1011113 isn't the specialization should come with the header of class? i.e. `Bar.h` should include `class bar;` and the specialization `template <> void foo();`, not `Foo.h`. – apple apple Dec 07 '22 at 14:17

2 Answers2

1

Yes, this would technically still an ODR violation (presuming that the template definitions are not identical in both translation units).

You'll probably get away with this, with most C++ compilers, but this is still a technical ODR violation -- the same template has a different definition in different translation units.

If, on the other hand, you turn it into two template specializations (and not even define the template itself), then it would not be an ODR violation. This is what specialized templates are for.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • 1
    Using two explicit specializations is already what OP did, but they technically require declarations before they are used in other translation units, which is why it isn't that straight-forward. (Forward declarations solve it however.) – user17732522 Aug 25 '22 at 12:33
  • The proposed code was two different template definitions, and an explicit template instantiation for both definitions. Additionally, details matter: the question was edited, with a notable change; but the answer's the same. – Sam Varshavchik Aug 25 '22 at 12:49
0

Based on the feedback from the comments, the answer is yes, this is an ODR violation.

To solve the original problem, a better solution is to declare the template specializations in the header. To avoid including the dependencies bar and baz, it's enough to forward-declare them.

user1011113
  • 1,114
  • 8
  • 27