首页 > 解决方案 > 如何为具有自定义类型的 std::vector 创建显式模板实例化声明?

问题描述

我正在尝试std::vector使用自定义类型创建显式模板实例化。

//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 */

另外,我有一个带有外部实例化声明的包装器

//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.cpp
#include "test_include.hpp"

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

主文件:

#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;
}

当我尝试构建它时,我看到以下错误:

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

令人惊讶的是,如果Test该类具有默认 ctor,则代码会成功编译。

我应该添加或更改什么才能使其正常工作?

编译器版本:clang 11。

标签: c++c++11templates

解决方案


根据标准,当您显式实例化模板时,类的所有非模板化成员都会被实例化(与来自每个 [temp.inst] 的隐式实例化的按需实例化相比)。

[temp.explicit] *

命名类模板特化的显式实例化也是其每个直接非模板成员的相同类型(声明或定义)的显式实例化,这些非模板成员先前未在包含显式实例化的翻译单元中显式特化,前提是该成员的关联约束(如果有)由显式实例化的模板参数满足

问题是这样vector的非模板成员resize需要T默认构造。MSVC 编译器在其错误消息中使这一点更加明显:

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

修复

标准语*中的关键短语是

[...] 前提是该成员的相关约束(如果有)由显式实例化的模板参数满足

问题似乎在于,它的实现vector还没有赶上 C++20。类似的函数resize不受例如 的约束requires DefaultConstructible<T>。查看 libstdc++ 的向量来源证实了这种情况。

实际的解决方案可能是“等待它”。

同时,如果您可以为您的类提供默认构造函数,请执行此操作。否则,您不能使用显式模板实例化。


* 关于约束的措辞在 C++20 之前不存在,因此如果您希望最终修复此问题,您还需要升级您的 C++ 版本


推荐阅读