Home > Blockchain >  How to create an explicit template instantiation declaration for std::vector with custom type?
How to create an explicit template instantiation declaration for std::vector with custom type?

Time:10-16

I am trying to create an explicit template instantiation for std::vector with a custom type.

//test.hpp

#ifndef TEST_HPP_JXIGJWWK
#define TEST_HPP_JXIGJWWK

template<typename T>
struct Test {
    Test(int) {}
    T value;
};

#endif /* end of include guard: TEST_HPP_JXIGJWWK */

Also, I have a wrapper with an extern instantiation declaration

//test_include.hpp

#ifndef TEST_INCLUDE_HPP_IBQ8DOTW
#define TEST_INCLUDE_HPP_IBQ8DOTW

#include <vector>
#include <string>
#include "test.hpp"

extern template struct Test<std::string>;
extern template class std::allocator<Test<std::string>>;
extern template class std::vector<Test<std::string>>;

#endif /* end of include guard: TEST_INCLUDE_HPP_IBQ8DOTW */

Another file where I have explicit template definitions:

another.cpp
#include "test_include.hpp"

template struct Test<std::string>;
template class std::allocator<Test<std::string>>;
template class std::vector<Test<std::string>>;

The main file:

#include "test_include.hpp"
#include "another.hpp"
#include <iostream>


int main() {

    std::vector<Test<std::string>> v;
    std::cout << v.size() << std::endl;

    return 0;
}

When I try to build it I see the following error:

In file included from /usr/include/c  /9/vector:65,
                 from /home/dima/extern_template_check/test_include.hpp:4,
                 from /home/dima/extern_template_check/another.cpp:1:
/usr/include/c  /9/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = Test<std::__cxx11::basic_string<char> >; _Args = {}]’:
/usr/include/c  /9/bits/stl_uninitialized.h:545:18:   required from ‘static _ForwardIterator std::__uninitialized_default_n_1<_TrivialValueType>::__uninit_default_n(_ForwardIterator, _Size) [with _ForwardIterator = Test<std::__cxx11::basic_string<char> >*; _Size = long unsigned int; bool _TrivialValueType = false]’
/usr/include/c  /9/bits/stl_uninitialized.h:601:20:   required from ‘_ForwardIterator std::__uninitialized_default_n(_ForwardIterator, _Size) [with _ForwardIterator = Test<std::__cxx11::basic_string<char> >*; _Size = long unsigned int]’
/usr/include/c  /9/bits/stl_uninitialized.h:663:44:   required from ‘_ForwardIterator std::__uninitialized_default_n_a(_ForwardIterator, _Size, std::allocator<_Tp>&) [with _ForwardIterator = Test<std::__cxx11::basic_string<char> >*; _Size = long unsigned int; _Tp = Test<std::__cxx11::basic_string<char> >]’
/usr/include/c  /9/bits/stl_vector.h:1603:36:   required from ‘void std::vector<_Tp, _Alloc>::_M_default_initialize(std::vector<_Tp, _Alloc>::size_type) [with _Tp = Test<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<Test<std::__cxx11::basic_string<char> > >; std::vector<_Tp, _Alloc>::size_type = long unsigned int]’
/home/dima/extern_template_check/another.cpp:5:21:   required from here
/usr/include/c  /9/bits/stl_construct.h:75:7: error: no matching function for call to ‘Test<std::__cxx11::basic_string<char> >::Test()’
   75 |     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /home/dima/extern_template_check/test_include.hpp:6,
                 from /home/dima/extern_template_check/another.cpp:1:
/home/dima/extern_template_check/test.hpp:6:5: note: candidate: ‘Test<T>::Test(int) [with T = std::__cxx11::basic_string<char>]’
    6 |     Test(int) {}
      |     ^~~~
/home/dima/extern_template_check/test.hpp:6:5: note:   candidate expects 1 argument, 0 provided
/home/dima/extern_template_check/test.hpp:5:8: note: candidate: ‘Test<std::__cxx11::basic_string<char> >::Test(const Test<std::__cxx11::basic_string<char> >&)’
    5 | struct Test {
      |        ^~~~
/home/dima/extern_template_check/test.hpp:5:8: note:   candidate expects 1 argument, 0 provided
/home/dima/extern_template_check/test.hpp:5:8: note: candidate: ‘Test<std::__cxx11::basic_string<char> >::Test(Test<std::__cxx11::basic_string<char> >&&)’
/home/dima/extern_template_check/test.hpp:5:8: note:   candidate expects 1 argument, 0 provided

Surprisingly if the Test class has a default ctor, the code compiles successfully.

What should I add or change to make it work?

Compiler version: clang 11.

CodePudding user response:

Per the standard, all non-templated members of a class get instantiated when you explicitly instantiate a template (vs as-needed instantiation that comes from implicit instantiation per [temp.inst]).

[temp.explicit]*

An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its direct non-template members that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, provided that the associated constraints, if any, of that member are satisfied by the template arguments of the explicit instantiation

The problem is that vector has non-template members like resize that require T to be default constructible. The MSVC compiler makes this more obvious in its error message:

vector(1191): note: while compiling class template member function 'void std::vector<Test<std::string>,std::allocator<Test<std::string>>>::resize(const unsigned __int64)'

The Fix

The key phrase in the standardese* is

[...] provided that the associated constraints, if any, of that member are satisfied by the template arguments of the explicit instantiation

The problem, it seems, is that implementations of vector haven't caught up to C 20 yet; functions like resize are not constrained with e.g., requires DefaultConstructible<T>. Looking at libstdc 's soure for vector confirms this is the case.

The actual solution will probably be to "just wait it out".

In the meantime, if you can provide a default constructor for your class, do it. Otherwise, you cannot use explicit template instantiation.


* the wording about constraints is not present pre-C 20, so you will also need to upgrade your version of C if you want this to eventually be fixed

  • Related