Linking problem with implicit instantiation of constructor/destructor

Hello,

I've run into wieird problem when trying to compile and link our programs with
clang++ on linux instead of g++. It occurs, when:
- template class member definitions are separated from the definition of the
class
- no explicit instantiation is done
- member definitions are available only in some of the units, where the
template is being used.
(Yeah, our code is a mess)

This is simple expamle:
// ----- template.h --------
template< typename T >
struct Template {
  Template();
  ~Template();
};

void doSomething(Template<int>& t);

// ----- template.cpp --------
#include "template.h"

template< typename T >
Template< T >::Template() { }

template< typename T>
Template< T >::~Template() { }

void doSomething(Template<int>& t) {
  Template<int> new_t;
  t = new_t;
}

// ----- main.cpp --------
#include "template.h"

int main(int argc_, char** argv_) {

  Template<int> t;
  doSomething(t);

  return 0;
}
// ----- end of code

When compiled with clang++:
$ clang++ -o test main.cpp template.cpp
/usr/bin/ld: /tmp/main-e2fa2c.o: in function `main':
main.cpp:(.text+0x2f): undefined reference to `Template<int>::Template()'
/usr/bin/ld: main.cpp:(.text+0x4d): undefined reference to
`Template<int>::~Template()'
/usr/bin/ld: main.cpp:(.text+0x82): undefined reference to
`Template<int>::~Template()'

reading the object files using nm tool shows, the symbol for destructor
instantiated in template.cpp is _ZN8TemplateIiED2Ev, but the main.o requires
symbol _ZN8TemplateIiED1Ev. Notice the difference in one digit: D2 vs D1.

So symbol ...D1... is required, but only ...D2... is available. The D1 version
is generated by clang only for explicit instantiation. g++ generates both D1
and D2 for any type of instantiation.

Can this be considered a bug of clang++? Or does this behaior have some
purpose?

Regards,
JZ

$ clang++ -o test main.cpp template.cpp

/usr/bin/ld: /tmp/main-e2fa2c.o: in function main': main.cpp:(.text+0x2f): undefined reference to Template::Template()’
/usr/bin/ld: main.cpp:(.text+0x4d): undefined reference to
Template<int>::~Template()' /usr/bin/ld: main.cpp:(.text+0x82): undefined reference to Template::~Template()’

What happens if you change the order of the .cpp files, putting template.cpp first; is it stil unresolved?

clang++ -o test template.cpp main.cpp

I don’t believe this code is valid according to C++. I believe it would require an explicit instantiation of the ctor/dtor somewhere to make that code valid - though I don’t have chapter and verse on the spec at hand just now to back that up.

What happens if you change the order of the .cpp files, putting
template.cpp first; is it stil unresolved?

clang++ -o test template.cpp main.cpp

The order doesn't matter.

I don't believe this code is valid according to C++. I believe it would
require an explicit instantiation of the ctor/dtor somewhere to make that
code valid - though I don't have chapter and verse on the spec at hand just
now to back that up.

I tried to read the c++ specs, but didn't find anything, that would clearly
state, if this is correct or incorrect. But the specs are too complicated for
me to understand.

My opinion is that it is bad to rely on implicit instantiation to happen
somewhere and don't do the explicit one. So I've already fixed all these
problems in our code and now I am just curious, what others think about it.

JZ.

Yeah, can’t seem to divine the concrete wording here either - perhaps Richard will have a moment to chime in.

In the latest draft, this is [temp.pre]/10:

“”"
A definition of a function template, member function of a class template, variable template, or static data member of a class template shall be reachable from the end of every definition domain (6.3) in which it is implicitly instantiated (13.9.1) unless the corresponding specialization is explicitly instantiated (13.9.2) in some translation unit; no diagnostic is required.

“”"

… which might be slightly easier to read in the pre-C++20 version (without the modules-awareness), where it is [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.

“”"