Search code examples
c++templateslinkerc++17explicit-instantiation

How to export SFINAE-constrained ctor defined in cpp file for explicitly instantiated template class?


Here's my attempt:

  • The header defining the Foo template class and declaring but not defining a ctor only for a given value of the NTTP N,
    // foo.hpp
    #include <type_traits>
    
    template<int N>
    struct Foo {
        template<int M = N, std::enable_if_t<M == 2, int> = 0>
        Foo(int);
    };
    
  • the cpp file defining that ctor and explicitly instantiating Foo for a value that corresponds to the ctor not being SFINAEd out,
    // foo.cpp
    #include "foo.hpp"
    
    template<int N>
    template<int M, std::enable_if_t<M == 2, int>>
    Foo<N>::Foo(int) {}
    
    template struct Foo<2>;
    
  • the main TU, that instantiates an object of Foo<2> class,
    // main.hpp
    #include "foo.hpp"
    
    int main() {
        Foo<2>{0};
    }
    

If I try to compile the two TUs above and link them, I get the following error from the linker:

main.cpp:(.text+0x24): undefined reference to `Foo<2>::Foo<2, 0>(int)'
collect2: error: ld returned 1 exit status

Indeed, I don't see any symbols from foo.cpp, as this

nm foo.o

gives no output.


If I add another, un-constrained ctor, e.g.

// foo.hpp
// …
struct Foo {
    Foo(int, int);
// …

and

// foo.cpp
// …
template<int N>
Foo<N>::Foo(int, int) {}
// …

then I do get what I expect:

nm foo.o | c++filt
0000000000000000 W Foo<2>::Foo(int, int)
0000000000000000 W Foo<2>::Foo(int, int)
0000000000000000 n Foo<2>::Foo(int, int)

Solution

  • Explicitly instantiate template class doesn't instantiate template member function.

    You need to explicitly instantiate those methods too:

    // template Foo<2>::Foo<2, 0>(int); // Not supported by clang
    template Foo<2>::Foo(int);
    

    Demo Demo