【问题标题】:Customizing std::allocator_traits::construct自定义 std::allocator_traits::construct
【发布时间】:2020-03-18 00:24:25
【问题描述】:

我想自定义 std::vector 行为以不默认构造元素类型(例如 int),因为这样做对于大型向量来说很昂贵。

看着这个,我能看到的唯一方法是专门化std::allocator_traits<MyAllocator>::construct。但是,这似乎是不可能的,因为特化必须与原始声明在同一个命名空间中。

namespace std 中设置专业化似乎并不正确。而且实际上比这更糟糕,因为我使用的 STL 实现实际上将 std::allocator_traits 放在了 namespace std::__u 中(而且这肯定会因 STL 实现而异),所以这样做似乎是非常错误的。

这令人困惑,因为似乎 std::allocator_traits 旨在允许专业化,但我无法弄清楚如何实际做到这一点。这只是一个坏主意吗?如果是这样,是否有其他方法可以解决问题(避免在 STL 容器中默认构造元素)?

【问题讨论】:

  • int 的默认初始值设定项是什么都不做,但大多数vector 重载实际上执行“零”初始化。 stackoverflow.com/questions/29765961/…
  • 为什么要构造大量没有价值的int
  • 实际上,鼓励使用专门的标准库模板(除非另有说明);另一方面,重载标准库函数是未定义的行为。
  • @MooingDuck 我会说任何合规的植入都会正确初始化vector 中的数据。在 90 年代实现 C++ 98 或 03 标准之前,可能会找到未正确初始化数据的编译器...
  • @Phil1970:我想你误会我了。 “零”初始化是一种特殊形式的初始化,其中 C++ 将使用 0 值初始化基元,并默认构造类。 Wheras“默认”初始化将默认构造类,但不会使用任何特定值初始化原语,因此从它们读取将是未定义的行为。请参阅我附加的链接。

标签: c++ stl allocator


【解决方案1】:

不仅允许专门化标准库特征类,而且是提供此类功能的主要方式。但是,在这种特殊情况下,这是不必要的。

默认的std::allocator_traits<T>::construct 实现(其中T 是您的分配器类型,而不是正在使用的容器的值类型)将调用Tconstruct 成员函数,如果T 有这样的函数,如果T 没有合适的成员,它会调用pl​​acement-new。所以只需给你的分配器一个construct 成员,你应该没问题。

【讨论】:

  • 感谢您的指点,我应该补充说我已经考虑过这一点,但这意味着例如对于std::vector<int>,我需要提供一个int的虚拟类(有很多运算符重载),这有点痛苦。
  • "专门化标准库特征类不仅是允许的,而且是提供此类功能的主要方式。但是,在这种特殊情况下,这是不必要的。"你能解释一下你实际上是如何为 std::allocator_traits 做的吗?
  • @dsharlet:有问题的T 不是vector 采用的T;它是分配器类型。我是说给你的分配器一个construct 模板函数。它应该只是有一个重载,当只给出一个指向要初始化的内存的指针时,它使用new value_type而不是new value_type()
  • 哦,这太完美了。谢谢!这比我尝试的其他所有方法都简单得多,我不知道我是怎么错过的!
  • std::allocator 方法 construct() 在 C++17 中被弃用,在 C++20 中被移除。因此,要做你想做的事情必须在你的分配器特征专业化的construct() 方法中实现。要真正完全符合 C++17/20,您还必须实现分配器及其分配器特征。
【解决方案2】:

您应该致电 reserve 而不是 resize。仅当值已知时才添加元素。没有必要创建一个充满垃圾的vector

std::vector<int> v;
r.reserve(500);
v.push_back(3); // Only 1 constructor is called

如果您确实不想初始化数据但仍要填充它,那么应该使用带有不初始化其成员的构造函数的struct

struct unintialized_int
{
    unintialized_int() { /* no initialization */ }
    int uninitialized;
};

std::vector<unintialized_int> v;
v.resize(500);
v[22].uninitialized = 33;

但是,我不建议这样做,因为这会导致难以找到错误!
最好按预期使用矢量。

顺便说一句,要对像int 这样的普通类型产生影响,您必须拥有数千个项目或创建数千个向量。

要问自己的一些问题:

  • 您是否正在衡量发布版本的性能?调试构建可能会明显变慢。
  • 您是否对应用程序进行了概要分析以确定导致速度变慢的是构造函数?事实上,对于像int 这样的琐碎类型,编译器可能能够将初始化优化为memset
  • 您确定初始化对性能有任何可衡量的影响吗?

【讨论】:

    猜你喜欢
    • 2013-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-10
    • 1970-01-01
    • 2023-01-03
    • 1970-01-01
    相关资源
    最近更新 更多