Note: Several related questions (e.g., this one) ended up being marked as duplicates of this question. I am aware of this particular question and follow the solution in the corresponding answers. However, different compilers yield different behavior and I don't know why.
My library features a class template and I would like to offer instances for certain template arguments in a library, as the template requires some significant compilation time. The class template could look like this (stack.hpp
)
#ifndef MY_STACK
#define MY_STACK
template<class T>
class stack
{
public:
stack();
};
#endif
and its implementation resides in a corresponding stack.tpp
file
#ifndef MY_STACK_TPP
#define MY_STACK_TPP
#include <iostream>
template<class T>
stack<T>::stack()
{
std::cout << "My stack constructor!" << std::endl;
}
#endif
As I would like to offer support for only certain template arguments, my stack.cpp
creates the following explicit template instances:
#include "stack.hpp"
template class stack<double>;
template class stack<char>;
#include "stack.tpp"
This compiles with g++ and clang++ but there are differences in the symbols of resulting shared library:
g++ -std=c++11 -c stack.cpp -o stack.so
nm -C stack.so | grep stack
0000000000000049 t _GLOBAL__sub_I_stack.cpp
0000000000000000 W stack<char>::stack()
0000000000000000 W stack<char>::stack()
0000000000000000 n stack<char>::stack()
0000000000000000 W stack<double>::stack()
0000000000000000 W stack<double>::stack()
0000000000000000 n stack<double>::stack()
vs.
clang++-7 -std=c++11 -c stack.cpp -o stack.so
nm -C stack.so | grep stack
0000000000000050 t _GLOBAL__sub_I_stack.cpp
In my application, the constructor of such an explicitly instantiated class is not found using clang++, but it works fine with g++. I figure this basic MWE gives the reason. Can anyone tell me how I can get the constructor symbols for my class template using clang++?
Quoting [temp.explicit]/1 [emphasis mine]:
A class, function, variable, or member template specialization can be explicitly instantiated from its template. A member function, member class or static data member of a class template can be explicitly instantiated from the member definition associated with its class template. [..]
And, quoting [temp.explicit]/9 [emphasis mine]:
An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is an explicit instantiation definition of only those members that have been defined at the point of instantiation.
Thus, in the OPs example, the explicit instantiation definition of stack<T>
will not include an explicit instantiation definition of the constructor, as the explicit instantiation definition of stack<T>
is placed before providing the definition of it constructor via the .tpp
include.
Quoting [temp.point]/8 [emphasis mine]:
A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. A specialization for a class template has at most one point of instantiation within a translation unit. A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one-definition rule, the program is ill-formed, no diagnostic required.
Thus, for the cases wheres stack.cpp
includes two different points of instantiations, and where one is before and one is after the inclusion of stack.tpp
, then the program is ill-formed.
However, here it becomes a bit tricky, as the points of instantiations depends on how the class template and its member function (/constructor) is used. As covered by [temp.explicit]/9 quoted above, the explicit instantiation of stack<T>
will not result in an explicit instantiation definition of its constructor, and we will instead need to fall back on [temp.point] for details, particularly clauses 1, 2 and 4, on when its use context will lead to an instantiation point before the inclusion of stack.tpp
.
The stand-alone example in the question is not covered by any of these cases, and thus the program us not ill-formed.
Can anyone tell me how I can get the constructor symbols for my class template using clang++?
As the constructor is never used, it should never (need to) be instantiated at all, but it seems as if (from the OPs symbol dump) GCC does so anyway (which is not illegal) whereas clang does not. If one where to use/refer to the constructor after the inclusion of track.tpp
, then both GCC and clang naturally instantiates it (for the particular specialization that is used), as they are then required to do so.