自定义初始化数组与 std::make_unique
Custom initialize array with std::make_unique
假设我想创建一个std::unique_ptr<int[]>
,但我想将创建的数组初始化为自定义值:{1,2,3,4,5}
.
我可以使用new
并将原始指针传递给std::unique_ptr
构造函数,然后构造函数将拥有和管理它。
std::unique_ptr<int[]> ptr{ new int[5]{1,2,3,4,5} };
我的问题是,std::make_unique
可以以某种方式做同样的事情吗?
std::make_unique
有 3 个重载:
template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args ); // (1) for non-array type
template< class T >
unique_ptr<T> make_unique( std::size_t size ); // (2) for array type with unknown bounds
template< class T, class... Args >
/* unspecified */ make_unique( Args&&... args ) = delete; // (3) for array type with known bounds.
它们都不支持您想要的行为(请注意,第三个函数标记为delete
)。
您可以使用 (2) 并单独初始化数组的元素,也可以切换到std::vector
并使用 (1)。
hgminh的答案(特别是如果可能的话推荐vector
的部分)是正确的,但我只是想添加另一个选项。
如果数组边界是已知且固定的,而不是未知的边界可变长度 C 样式数组,则可以从 C 样式数组切换到std::array
来实现此目的。完全打开优化后,运行时工作是等效的(-O1
与g++
,它正确地确定它可以内联整个事情,使其成为普通分配,然后直接填充新分配内存中的各个元素,而不是尝试在堆栈上进行array
,然后将其作为参数传递给make_unique
, 这最终将调用移动构造函数,有效地将std::array<POD type>
的工作加倍)。你只需更改:
std::unique_ptr<int[]> ptr{ new int[5]{1,2,3,4,5} };
自:
auto ptr = std::make_unique<std::array<int, 5>>(std::array<int, 5>{1,2,3,4,5});
可悲的是,对于当前的非实验性功能集,这确实需要重复指定指向的类型(一次用于构造它,一次用于定义make_unique
的模板化类型),因为make_unique
不接受初始值设定项列表,因此您必须在语法上构造临时,即使优化器避免它。对于这种特殊情况,您可以使用实验性功能来避免重复自己,但它并没有更漂亮(如果您不使用using
语句来避免指定命名空间,实际上更长):
auto ptr = std::make_unique<std::array<int, 5>>(std::experimental::make_array(1,2,3,4,5));
与 C 样式数组相比,std::array
的主要优点和缺点是,无论哪种方式,最终结果都是std::unique_ptr<std::array<int, 5>>
,而不是std::unique_ptr<int[]>
;一方面,指向的数组的大小永远不会改变(你以后不能用指向std::array<int, 6>
的指针替换unique_ptr
内容),但另一方面, 大小在编译时烘焙,因此您和编译器都知道大小。
由于编译器知道大小,因此在调用基于其参数类型模板化的函数时,不必手动传递指针和大小。该模板将在编译时专用于您的精确大小(这允许编译器在循环展开或使用常量循环边界时做出更好的优化选择),而无需传递大小。
对于未模板化且需要 C 样式参数的函数(例如,它们需要一个数组,并接收第一个元素的int*
和长度size_t
),您只需将&ptr[0]
作为指针传递,ptr->size()
作为长度传递。由于大小是一个编译时常量,这让你免费 DRY(没有在多个地方重复数组的大小,也不是你定义相当无用的命名常量只是为了避免 DRY;大小是类型定义的一部分,内联使用,在上下文中具有明显的含义),没有性能开销(它应该内联到编译时大小,就像你自己键入大小一样, 但是,如果以后更改array
的大小,则不会有数字不同步的风险)。
同样,为了绝对清楚,这里的正确答案几乎总是"使用std::vector<int>
">,这类似于std::unique_ptr<int[]>
:
- 根据需要自动调整
int[]
大小 - 显著简化常见用例(例如初始化、复制、移动等)
当大小没有主动更改时,std::vector
可以很好地与 C 风格的数组 API 一起使用(传递vec.data()
/&vec[0]
/&vec.at(0)
作为指针,传递vec.size()
作为长度),并且您无需担心管理大小调整/重新分配(当您不能在不放弃访问delete[]
的情况下使用realloc
时,这在C++中很痛苦)。从理论上讲,它可以稍微慢一点,但在 99% 的情况下,它会比必须从头开始重新实现类似vector
行为的任何行为都要快(因为vector
被调整为以最大速度"正常工作",而您自己的代码不太可能经过仔细调整)。
- 为什么 std::unique 不调用 std::sort?
- VS Code "command":"make"与终端窗口中的命令行"make"不同
- 使用 make 编译 MPI,几个命名空间错误,例如"错误:未知类型名称'使用'?
- make 命令如何避免重新编译未更改的源文件?
- MAKE:找不到包含的用户定义的头文件?
- 'make check' GLIBC 运行时的链接问题
- 生成"unique"矩阵
- Qt5 [make -snap] 无法正确编译:进程"/usr/bin/snap"代码 1 退出
- mingw32-make 使用"MinGW Makefiles"生成器跟踪 CMAKE 无法将可执行文件链接到对象库
- 我对 std::unique(算法)C++有问题
- make 命令创建 .file,但不创建应用程序文件
- 如何摆脱导入的 make 项目中的 Eclipse 索引器"Type std::... could not be resolved"错误
- Qt Creator 在执行步骤 "make" 时出现编译错误,-fno-stack-limit
- 如何使用MySQL Connector and Make设置C++项目
- 使用 make 将对象文件放在特定目录中
- 我是 C++ 的新手,我试图调用 make 一个以 2 个类作为其参数的类构造函数
- "Make"失败并出现 Clang 错误 - 如何从 Clang 获得错误?
- 防止 GNU Make 在每次构建时生成 protobuf 代码
- Make zmqpp::socket::connect a std::future
- 链接从命令行转换为Make的库