如何将std::uses_allocator_construction_args展开为构造函数参数列表?
需要实现一个可模板化的Entity类,满足以下需求:
- 适配两种字符串类型:拥有所有权的
std::string,以及无所有权的std::string_view - 类内部包含一个需要分配器的
vector数据结构 - 当模板参数为
std::string时:- 内部
vector与字符串使用相同的分配器 Entity构造函数需将分配器转发给std::string对象
- 内部
- 当模板参数为
std::string_view(通过适配器封装)时:- 构造函数不接受分配器,将字符串初始化为空
尝试使用std::uses_allocator_construction_args生成构造参数,但无法将返回的元组解包到构造参数列表中,需要解决这个问题。
用户提供的初始实现代码:
#include <iostream> #include <vector> #include <string> #include <string_view> template <typename Allocator> struct view_adapter { using string_type = std::string_view; using allocator_type = Allocator; }; template <typename StringType> struct string_type_of_or { using type = StringType; }; template <typename Allocator> struct string_type_of_or<view_adapter<Allocator>> { using type = view_adapter<Allocator>::string_type; }; template <typename StringType> struct Entity { using allocator_type = StringType::allocator_type; using string_type = string_type_of_or<StringType>::type; Entity(allocator_type allocator = {}) : vec{ allocator } , str{ uses_allocator_construction_args<StringType>(allocator)... } // 此处无法解包元组 {} StringType str; std::vector<StringType, typename std::allocator_traits<allocator_type>::template rebind_alloc<StringType>> vec; }; int main() { Entity<std::string> my_val; Entity<view_adapter<std::allocator<std::string_view::value_type>>> my_view; }
核心问题分析
std::uses_allocator_construction_args返回的是参数元组,无法直接用...展开作为构造参数。需要用std::make_from_tuple来从元组构造对象。另外,需要让标准库识别view_adapter不需要分配器,这样uses_allocator_construction_args会生成空参数元组,构造空的std::string_view。
修改后的实现代码
#include <iostream> #include <vector> #include <string> #include <string_view> #include <memory> // 包含make_from_tuple和uses_allocator_construction_args template <typename Allocator> struct view_adapter { using string_type = std::string_view; using allocator_type = Allocator; }; // 特化std::uses_allocator,让view_adapter不使用分配器 namespace std { template <typename Alloc> struct uses_allocator<view_adapter<Alloc>, Alloc> : false_type {}; } template <typename StringType> struct string_type_of_or { using type = StringType; }; template <typename Allocator> struct string_type_of_or<view_adapter<Allocator>> { using type = typename view_adapter<Allocator>::string_type; }; template <typename StringType> struct Entity { using allocator_type = typename StringType::allocator_type; using string_type = typename string_type_of_or<StringType>::type; Entity(allocator_type allocator = {}) : vec{ allocator } , str{ std::make_from_tuple<StringType>( std::uses_allocator_construction_args<StringType>(allocator) ) } {} StringType str; std::vector<StringType, typename std::allocator_traits<allocator_type>::template rebind_alloc<StringType>> vec; }; int main() { Entity<std::string> my_val; Entity<view_adapter<std::allocator<char>>> my_view; }
关键修改说明
用
std::make_from_tuple解包元组:std::make_from_tuple<StringType>(tuple)会自动将元组中的元素作为构造参数传递给StringType的构造函数,解决了元组无法直接展开的问题。特化
std::uses_allocator:
为view_adapter特化std::uses_allocatortrait,返回false。这样std::uses_allocator_construction_args会认为view_adapter不需要分配器,生成空的参数元组,最终构造出空的std::string_view(因为std::string_view的默认构造就是空的)。修正类型别名的
typename:
原代码中string_type_of_or的type定义缺少typename,在模板中访问嵌套类型必须加typename,否则会编译报错。
内容的提问来源于stack exchange,提问作者glades

