Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extern template for template parametrized with incompete type

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.

like image 328
Smiles Avatar asked Dec 22 '14 19:12

Smiles


People also ask

When is a non-type template parameter unmodifiable?

When the name of a non-type template parameter is used in an expression within the body of the class template, it is an unmodifiable prvalue unless its type was an lvalue reference type, or unless its type is a class type (since C++20) .

What is an extern template in C++11?

In C++11, extern template feature is introduced to avoid this situation. Above will tell the compiler to not instantiate template function or class as it is being instantiated somewhere else. Programmer should use this if and only if above template function or class is instantiated somewhere else.

What is a template parameter in C++?

An identifier that names a non-type template parameter of class type T denotes a static storage duration object of type const T, called a template parameter object, whose value is that of the corresponding template argument after it has been converted to the type of the template parameter.

What are the different types of template parameters?

Template parameters can be types, non-types, and templates. Okay, types are the most often used template parameters. Here are a few examples: Integrals are the most used non-types. std::array is the typical example because you have to specify at compile time the size of a std::array: Templates can be template parameters.


1 Answers

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;
}
like image 114
Dietmar Kühl Avatar answered Sep 21 '22 15:09

Dietmar Kühl