Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Is template specilization in different source file undefined behavior?

header.h:

#include <iostream>

template<typename T>
class Test {
public:
  __attribute__((noinline)) void fun(int a, int b) {
    std::cout << a + b << std::endl;
  }
};

class SpecialClass {};

a.cpp:

#include "header.h"

int main() {
  Test<SpecialClass> test;
  test.fun(10, 8);

  return 0;
}

b.cpp:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

#include "header.h"

template<>
void Test<SpecialClass>::fun(int a, int b) {
  std::cout << a - b << std::endl;
}

g++ version: 8.3.1

Compile this program without optimization:

$ g++ -O0 a.cpp b.cpp -o pro_O0
$ ./pro_O0
2

The output is 2, which means the specilized version is called.

Compile this program using -O3:

$ g++ -O3 a.cpp b.cpp -o pro_O3
$ ./pro_O3
18

The specilized version is NOT used in this case.


I checked the symbol in object file:

When using -O0:

$ nm -C a_O0.o | grep fun
0000000000000000 W Test<SpecialClass>::fun(int, int)
$ nm -C b_O0.o | grep fun
0000000000000000 T Test<SpecialClass>::fun(int, int)

Test<SpecialClass>::fun(int, int) in a.o is a weak symbol, so it is override by the symbol in b.o at link time.

When using -O3:

$ nm -C a_O3.o | grep fun
0000000000000000 t Test<SpecialClass>::fun(int, int) [clone .isra.1] [clone .constprop.2]
$ nm -C b_O3.o | grep fun
0000000000000000 T Test<SpecialClass>::fun(int, int)

Test<SpecialClass>::fun(int, int) in a.o become a local symbol, which can not be override by the symbol in b.o


So I wonder

  1. Is this a undefined behavior? If it is, what’s the related standard rules?
  2. How to solve this problem if I want to always use the specialized version? Except putting them in a single file, I known this indeed take effect, but my real program is much more complex and difficult to do large refactor.

I have tried add __attribute__((weak)), it seems has no effect on this case, but works on my real program.

>Solution :

Is this a undefined behavior? If it is, what’s the related standard rules?

Yes, it is ill-formed, no diagnostic required (IFNDR), effectively meaning the same as undefined behavior.

[temp.expl.spec]/7 from the post-C++20 standard draft N4868:

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.

In your translation unit for a.cpp there is no declaration of the explicit specialization at all, but the call to fun causes implicit instantiation of the specialization.

How to solve this problem if I want to always use the specialized version? Except putting them in a single file, I known this indeed take effect, but my real program is much more complex and difficult to do large refactor.

By declaring (but not defining) the explicit specialization in the header file:

template<typename T>
class Test {
public:
  __attribute__((noinline)) void fun(int a, int b) {
    std::cout << a + b << std::endl;
  }
};

class SpecialClass {};

// explicit specialization declaration
template<>
void Test<SpecialClass>::fun(int a, int b);

The explicit specialization definition can then stay in b.cpp.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading