容器优化:为什么STL容器方法参数不再使用allocator :: const_reference typedef?

时间:2014-07-30 07:20:30

标签: c++ c++11 stl containers allocator

在您阅读之前: const_reference是typedef,不需要const T&就像您在std::vector<bool>::const_reference = bool中看到的那样。请在阅读其他内容时牢记这一点,以便正确理解(正如值得一提的,这对许多人来说很难)。


我想将STL容器用于简单类型(例如int)并发现它们使用次优const T& &#34;反模式&#34; - 它适用于大类,但在没有内联时对于简单/基本类型是次优的 - 考虑嵌入式系统,例如在ARM / ATSAM4L上,实例化。

问题是:为什么例如自{+ 1}}以来,vector::push_back重新设计了(const value_type&)而不是(Allocator::const_reference),因为C ++ 11? 对于通用allocator,它应该是相同的,但是以另一种方式执行它将帮助我为基本类型编写自定义分配器(或模板特化),以定义const_reference作为类型本身(请参阅{ {3}})。

问题2:是否有任何适配器类可以为我做这件事?

这样的事情:

template<class T, class base = std::vector<T> >
  class myvect: protected base {
public:
    typedef T const_reference;
    void push_back(const_reference value) {
        base::push_back(value); }}

最终用法将是这样的:

typedef void (*action_t)(void*,int);
extern "C" void work(void *, action_t);
work(&vect, (action_t)&vect::push_back);

注意:忽略以前代码块中可能存在的转换问题,希望您明白这一点。)

编辑: vector::const_reference直接定义为const value_type&但在我看来应定义为Alloc::const_reference,然后可以轻松更改{{1} }})。 (这在C ++ 11中有所改变,它被定义为Alloc :: const_reference但现在是const value_type&amp;)

编辑: vector<int, MyAllocator<int> >有时在stackoverflow上被描述为 &#34;反模式&#34; ,因为它对于没有内联的基本类型来说,它是次优的(是的,如果内联,编译器会为func(const T&)生成最佳代码,但是 IF func(const int&)会做得更好)


结论:问题似乎出现在func(int)的行为中,因此constconst T&并不意味着不会更改,但不会更改,因此需要const(以及明确定义和使用) as许多方法的返回类型。创建自定义适配器类似乎是优化的最佳方式,似乎被广泛接受,const T&是奇怪的异常,应该在单独的类中(例如vector<bool>::const_reference = bool

3 个答案:

答案 0 :(得分:6)

评论中有点长,所以我会使用&#34;答案&#34;谈论它,但实际上这不是一个答案。

您想要的功能,重新编写:

你希望te能够避免&#34;通过const引用&#34;某些方法中的成语,以避免&#34;减速&#34;由小型类型的指针操作引入,而不是直接复制。对于push_back尤其如此,如果push_back具有可配置类型的参数(通过模板在容器类型中的意思,比如allocator模板参数),那么它可以让您感觉良好。

有可能提供您想要的功能,当然,例如通过使用与allocator模板参数相同的方法,可能有一个typename指定在&#34中使用的类型;推回&# 34;参数。

但它不是那样设计的,因为它似乎是一种复杂性,根本不值得。 由于编译器优化会消除基本类型的大量开销,因此不值得,因为静态分析更容易在基本类型上运行。

其次,&#34;失去的时间&#34;通过引用传递int与在方法参数中复制它被认为是平台特异性,无法在虚拟机&#34;级别(语言编写者必须自己放置的级别)。更不用说&#34;过早优化&#34;这将导致坦率的代码臃肿获得,作为一般目的的合理权衡,它被彻底驱逐。这也是因为在设计时,你会想到基本类型,如稀有性和可能包含的可能类型的无限性。

PS: 最后,使用typedef作为分配器的一部分似乎是一个非常糟糕的责任分离,拥有自己的模板参数似乎更清晰,因为它与分配无关,而是与实现选择特定方法有关。容器

PS2 /编辑: 我想补充一点,正如评论中所说,vector<bool>::const_reference不能作为可能的例子,因为它是标准中的一个错误,实际上是对STL的侵犯要求容器将..::reference定义为T&。问题的进一步证明是vector<bool>::reference(不是const)不是bool,它是一个未指定的类(gcc中的_Bit_reference),用作访问和变异的代理,打包在向量,它将作为&#34;虚拟&#34;的存储支持。 bool值,使其像现有的那样出现。这些奇怪之处不能移植到通用向量中,因为它们不能满足广泛的STL要求。但它并没有使正交方法无效,就像我在pass_for_copy_param_type中提到的那样,它可以是一个可参数化的新typedef,并且在某些地方(合适的地方)用于替换,其中&#34; const value_type&amp;&#34;写于今天。但这正是我之前提到的代码膨胀。对于这么少的人来说,我肯定不值这么多。

答案 1 :(得分:4)

更改const_reference的含义会破坏返回const引用的方法的语义:

std::vector<int> my_vector(100);
 // this needs an lvalue ref
my_vector[42]++;

// I want to track this value
std::vector<int>::const_reference i = my_vector[3];
  // I expect i to be be 1 after this
my_vector[3]++;

答案 2 :(得分:2)

允许更改const_reference的类型会破坏与std :: vector和容器要求的兼容性。该标准明确假定const_reference是一个引用并使用了很多次,例如data()和front()的语义。在23.3.6.4中,它表示&#34;对于非空向量,data()==&amp; front()&#34;。这使得您的修改无效,因为修改后,front()将不再返回对已分配内存的引用,因此地址会有所不同。实际上,它将是临时对象的地址。

我还认为大多数编译器都会优化使用const和amp;的任何额外成本。程。

的std ::矢量&lt; bool&gt;是一个不好的例子,因为它不是STL意义上的容器模型。大多数人都同意它不应该在标准中定义,例如https://isocpp.org/blog/2012/11/on-vectorbool