9

A compilable example:

main.cpp

#include "test.h"

int main(int argc, char* argv[]) {
    auto myPtr = std::unique_ptr<MyClass>(getMyPtr());
}

test.h

#ifndef TEST_H
#define TEST_H

#include <memory>

class MyClass;
extern template class std::unique_ptr<MyClass>;
MyClass* getMyPtr();

#endif

test.cpp

#include "test.h"

class MyClass {};
template class std::unique_ptr<MyClass>;
MyClass* getMyPtr() { return new MyClass; }

g++ 4.9.2 complains

In file included from c:/devel/mingw32/i686-w64-mingw32/include/c++/memory:81:0,
                 from main.cpp:4:
c:/devel/mingw32/i686-w64-mingw32/include/c++/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = MyClass]':
c:/devel/mingw32/i686-w64-mingw32/include/c++/bits/unique_ptr.h:236:16:   required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = MyClass; _Dp = std::default_delete<MyClass>]'
main.cpp:64:53:   required from here
c:/devel/mingw32/i686-w64-mingw32/include/c++/bits/unique_ptr.h:74:22: error: invalid application of 'sizeof' to incomplete type 'MyClass'
  static_assert(sizeof(_Tp)>0,
                      ^

even though MyClass should be visible at the point of template instantiation. Why?

Edit: fixed a typo in example.

Smiles
  • 1,373
  • 1
  • 9
  • 12
  • `extern template class std::unique_ptr;` is this line mising a symbol? `extern template class std::unique_ptr my_symbol;` Also the other one from the `test.cpp`: `template class std::unique_ptr my_symbol;` – πάντα ῥεῖ Dec 22 '14 at 19:56
  • 3
    @πάνταῥεῖ: actually, it does not lack a symbol at all! This declaration tells the compiler that another translation unit will provide the instantiation of the corresponding class template! – Dietmar Kühl Dec 22 '14 at 19:58
  • @DietmarKühl Ah, THX for clarification. I didn't knew that syntax yet. I learn something new here every day :-D. – πάντα ῥεῖ Dec 22 '14 at 20:00

1 Answers1

6

The effects of instantiation declarations, i.e., a guarantee that the template is not instantiated implicitly, does not apply to inline functions according to 14.7.2 [temp.explicit] paragraph 10:

Except for inline functions, declarations with types deduced from their initializer or return value (7.1.6.4), 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 (3.2) 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 ]

The standard library is clearly free to declare any of its functions as inline. That is, using an instantiation declaration doesn't affect the requirement on types being defined with standard library template class (unless otherwise specified, of course). gcc defines the destructor for std::unique_ptr<...> in the definition of this class template making it implicitly inline. Here is an example source demonstrating the problem: depending on whether DECL_ONY is defined it compiler or not:

template <typename T>
struct foo
{
    ~foo()
#ifdef DECL_ONLY
        ;
#else
    { static_assert(sizeof(T), "defined!"); }
#endif
};

#ifdef DECL_ONLY
template <typename T>
foo<T>::~foo() { static_assert(sizeof(T), "defined!"); }
#endif

class MyClass;
extern template struct foo<MyClass>;

int main(int , char* []) {
    foo<MyClass> f;
}
Dietmar Kühl
  • 141,209
  • 12
  • 196
  • 356
  • 2
    I tried to extern template the default_delete too, got same message. – Smiles Dec 22 '14 at 20:04
  • 1
    @Cynic: fair enough. The problem is, indeed, something different: `inline` functions are exempted from veing externally instantiated and the functions are defined [implicitly] `inline`. – Dietmar Kühl Dec 22 '14 at 20:34
  • I just assumed that in this case an out-of-line version of function is generated, like with inline virtuals. Now I see that there was no reason to assume that (because of explicit instantiation being in a different compilation unit). – Smiles Jan 08 '15 at 23:12