Jan Špaček Jan Špaček - 1 year ago 51
C++ Question

Automatic constructor in explicitly instantiated class template

I have a

template<bool VAR> struct Obj
template declared in a header file (
) with explicit automatic move constructor (
= default

// obj.h
#pragma once
#include <vector>

template<bool VAR>
struct Obj {
std::vector<int> member;
Obj(int m): member(m) { }
Obj(Obj&&) = default;
int member_fun() const;

extern template struct Obj<false>;
extern template struct Obj<true>;

The member function of the template is defined in another file (
) with explicit instantiation of the template:

// obj.cpp
#include "obj.h"

template<bool VAR>
int Obj<VAR>::member_fun() const {
return 42;

template struct Obj<false>;
template struct Obj<true>;

This template is then used from the main file (

// main.cpp
#include <utility>
#include "obj.h"

int main() {
Obj<true> o1(20);
Obj<true> o2(std::move(o1));
return o2.member_fun();

s are then compiled and linked together with the following

CXXFLAGS=-Wall -Wextra -std=c++14

a.out: obj.o main.o
$(CXX) $(CXXFLAGS) $^ -o a.out

obj.o: obj.cpp obj.h
$(CXX) $(CXXFLAGS) -c $< -o $@
main.o: main.cpp obj.h
$(CXX) $(CXXFLAGS) -c $< -o $@

However, I get a linker error:
undefined reference to 'Obj<true>::Obj(Obj<true>&&)'
-- the compiler apparently did not instantiate the constructor.

  • Obj<true>::member_fun()
    is defined and the program indeed links successfully if I remove the reference to the move constructor from

  • If I remove
    extern template
    from the header, the program compiles.

  • If I use
    instead of
    for the type of
    , the program also compiles.

  • cppreference.com claims that "the compiler will declare a move constructor as a non-explicit inline public member of its class". However, the manually defined
    constructor is also inline, but it is correctly instantiated.

(I received this error with Clang in a project that compiled fine with GCC, so I thought this was a Clang bug. However, when I reduced the problem to this simple case, both GCC 5.4.0 and Clang 3.8.0 produce the same results.)

Answer Source

Interesting. I think your code is correct, because:

Your defaulted move-constructor is implicitly inline because of:


... A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted.

And [class.mfct]/1:

A member function may be defined ([dcl.fct.def]) in its class definition, in which case it is an inline member function ([dcl.fct.spec])

And thus is exempt from explicit template instantiation according to [temp.explicit]/10 (emphasis mine):

Except for inline functions and variables, declarations with types deduced from their initializer or return value ([dcl.spec.auto]), const variables of literal types, variables of reference types, and class template specializations, explicit instantiation declarations have the effect of suppressing the implicit instantiation of the entity to which they refer. [ Note: The intent is that an inline function that is the subject of an explicit instantiation declaration will still be implicitly instantiated when odr-used ([basic.def.odr]) so that the body can be considered for inlining, but that no out-of-line copy of the inline function would be generated in the translation unit. — end note ]

In fact, if you try any optimization mode other than -O0, the problem disappears.

-O0 is a special mode in which inlined functions are not inlined. But it shouldn't matter, the compiler must in that case generate the inline defaulted move-constructor like it does with the other constructor.

So to me this looks like a compiler bug. Also look at LLVM#22763

I see 2 possible workarounds:

Solution 1

Do not use extern template ... for now (compilation times will suffer but otherwise no big deal).

Solution 2 (not recommended)

Trick the compiler into generating the suppressed constructor.

template<> Obj<true>::Obj(Obj&&) noexcept = default;