Auto constructor in an explicitly created class template

I have a template template<bool VAR> struct Obj declared in the header file ( obj.h ) with an 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>; 

A member function of a template is defined in another file ( obj.cpp ) with an explicit template instance:

 // 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 ):

 // main.cpp #include <utility> #include "obj.h" int main() { Obj<true> o1(20); Obj<true> o2(std::move(o1)); return o2.member_fun(); } 

Then .cpp compiled and linked together with the following Makefile :

 #CXX=clang++ CXX=g++ 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 create an instance of the constructor.

  • Obj<true>::member_fun() defined, and the program really successfully connects if I remove the link to the move constructor from main.cpp .
  • If I remove the extern template from the header, the program compiles.
  • If I use int instead of std::vector<int> for type member , the program also compiles.
  • cppreference.com claims that "the compiler will declare the move constructor as an implicit inline public member of its class." However, the manually created Obj(int) constructor is also built-in, but it is correctly created.

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

+8
c ++ c ++ 11 templates
source share
3 answers

Interesting. I think your code is correct because:

Your default constructor is implicitly inline due to:

[dcl.fct.def.default] / 5 :

... A user-provided function with an explicit default (i.e., explicitly the default after the first declaration) is defined at the point where it is clearly defaulted.

And [class.mfct] / 1 :

A member function can be defined ([dcl.fct.def]) in its class definition, in which case it is a built-in member function ([Dcl.fct.spec])

And thus it is freed from explicit template creation in accordance with [temp.explicit] / 10 (highlighted by me):

With the exception of built-in functions and variables , declarations with types are inferred from their initializer or return value ([dcl.spec.auto]), literal type constant variables, reference type variables and specialized class templates, declarations with an explicit manifest have the effect of suppressing implicit creation of an entity to which they relate. [Note: the goal is that the built-in function that is the object of the explicit declaration of creation will still be implicitly created using odr ([basic.def.odr]) so that the body can be considered for attachment, but that there is no external copy of the built-in functions will be generated in the translation. - final note]

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

-O0 is a special mode in which built-in functions are not built-in. But it doesn’t matter, in this case the compiler should generate a default move inline constructor, as it does with another constructor.

So for me, this seems like a compiler error. See also LLVM # 22763 and GCC # 60796 .

I see at least 2 possible workarounds:

Solution 1

Do not use extern template ... at the moment (compilation time will suffer, but otherwise it does not really matter).

Decision 2

Trick the compiler into generating a depressed constructor in obj.cpp

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

This mode will only be used in -O0 mode. The production code will use the embedded version instead.

+4
source share

There is not much information on the Internet about this topic. Although I look at some sources, I would do the following:

extern template should prevent implicit instances, although in all the examples creating explicit does not have this extern template definition.

From what I can read, especially in the sentence and on the GCC mailing list (see links below), the extern template does not prevent the creation of implicit instances, although ALL are template instances. This will include your explicit instance.

If an object is an object as an explicit instantiation of the declaration and an explicit definition of the instantiation in the same translation unit, the definition must follow the declaration. - John Spicer on the GCC Mailing List

From this, I would conclude that you should remove the extern template in the translation block where explicit creation of the instance is required.

Literature:

+2
source share

You may have come across a compiler error.

See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60796 .

I find a similar behavior in CLang, but cannot find the error report.

0
source share

All Articles