c++ - 如何为具有自定义类型的 std::vector 创建显式模板实例化声明?

标签 c++ c++11 templates

我正在尝试使用自定义类型为 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 类有一个默认的构造函数,代码就能成功编译。

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

编译器版本:clang 11。

最佳答案

根据标准,当您显式实例化模板时,类的所有非模板化成员都会被实例化(相对于根据 [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

问题是 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)'

修复

标准语*中的关键词是

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

问题似乎是 vector 的实现还没有 catch C++20;像resize这样的功能不受例如 requires DefaultConstructible<T> 的限制.查看 libstdc++ 的源代码 vector证实了这一点。

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

与此同时,如果您可以为您的类提供默认构造函数,那就去做吧。否则,您不能使用显式模板实例化。


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

关于c++ - 如何为具有自定义类型的 std::vector 创建显式模板实例化声明?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69556553/

相关文章:

c++11 - 为什么 `vector<int> v{{5,6}};` 有效?我以为只允许一对 {}?

c++ - 使用类型特征为字符串迭代器专门化模板函数

c++ - 使用以下 has_member 函数时 SFINAE 无法正常工作的原因是什么?

c++ - 指向引用的指针是非法的

c++ - Hinnant 的堆栈分配器与 boost rtrees : compilation failure

c++ - add_lvalue_reference实现示例

c++ - 输入不在 C++ 中的 scanf 中终止

c++ - 编译器支持 STL 容器中的有状态分配器

c++ - 用 printf 打印一个字符

C++ 从 map 的第二个元素迭代