The following code (I couldn't make a shorter MVCE)
unit.h:
#include <vector>
template<typename T>
struct foo
{
std::vector<T> data;
foo(foo&&) = default; // no assembly generated
foo(std::vector<T>&&v) : data(std::move(v)) {}
};
extern template struct foo<int>; // indicates template instantiation elsewhere
unit.cc:
#include "unit.h"
template struct foo<int>; // forces template intantiation
main.cc:
#include "unit.h"
struct bar
{
foo<int> f;
bar(foo<int>&&x) : f(std::move(x)) {}
};
bar makeBar(int x)
{
std::vector<int> v(x);
foo<int> f(std::move(v));
return {std::move(f)};
}
int main()
{
bar x = makeBar(5);
}
fails to compile under clang (Apple LLVM version 9.0.0 (clang-900.0.39.2) -- which llvm version is that?) with the result:
test> clang++ -std=c++11 -c unit.cc
test> clang++ -std=c++11 -c main.cc
test> clang++ -std=c++11 main.o unit.o
Undefined symbols for architecture x86_64:
"foo<int>::foo(foo<int>&&)", referenced from:
bar::bar(foo<int>&&) in main-476e7b.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Everything works fine with gcc (8.2.0). On inspection, it appears that gcc emits foo<int>::foo(foo<int>&&)
in main.o
, while clang fails to emit it completely.
What is the correct behaviour: should the default
move constructor be emitted with unit.o
or main.o
? Is this a known clang bug?
useful link: https://en.cppreference.com/w/cpp/language/class_template
This is a clang bug. Your code is well formed so whatever would be the strategy of the compiler considering the "as if" rule, your code should compile.
Explicit instantiation of a class template only instantiates members for which a definition is provided [temp.explicit]/9:
Special member function defaulted on their first declaration are only defined when odr-used. So I suppose that the bug is that Clang expects that at the point of explicit instantiation, the defaulted constructor was also instantiated.
So the work around could be first, to declare the move constructor in the header file, then to define it as defaulted in the implementation file:
unit.hpp:
unit.cpp:
This will force the generation of the definition of the defaulted move constructor (see [dlc.fct.def.default]/5). The draw back is that the definition of
foo(foo&&)
is not inline anymore.Alternatively the solution bellow will work: