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



在你阅读之前: const_reference是类型定义的,不需要是const T&,因为你可以在std::vector<bool>::const_reference = bool中看到。请在阅读其他内容时记住这一点,以正确理解它(正如评论中建议的那样,这对许多人来说很难)。


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

问题是:为什么从c++ 11开始用(const value_type&)的参数而不是(Allocator::const_reference)来重新设计vector::push_back ?对于泛型的allocator应该是一样的,但是用另一种方式做会帮助我为基本类型编写一个自定义分配器(或模板专门化),将const_reference定义为类型本身(参见vector<bool>::const_reference = bool)。

问题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);

(注意:忽略前面代码块中可能存在的类型转换问题,我希望你明白。)

EDIT: vector::const_reference直接定义为const value_type&,但在我看来应该定义为Alloc::const_reference,然后可以很容易地更改(vector<int, MyAllocator<int> >)。(这在c++ 11中改变了,它被定义为Alloc::const_reference,但现在是const value_type&)

EDIT: func(const T&)有时在stackoverflow上被描述为"反模式",因为它对于没有内联的基本类型是次优的(是的,编译器即使为func(const int&)生成最优代码,如果它被内联,但这里是 if func(int)会做得更好)


结论:问题似乎在于const的行为,因此const T&const并不意味着不改变,而是不改变,因此const T&需要(并且定义和使用得很好)作为许多方法的返回类型。创建自定义适配器类似乎是优化的最佳方式,它似乎被广泛接受,std::vector<bool>是奇怪的例外,应该在单独的类(例如dynamic_bitset)。

这在评论中有点长,所以我将使用"答案"来谈论它,但实际上这不是一个答案。

您想要的功能,改写为:

您希望能够在某些方法中避免"通过const引用传递"的习惯用法,以避免与直接复制相比,小类型的指针操作引起的"减速"。特别是对于push_back,如果push_back有一个可配置类型的形参(通过模板方式在容器类型中,如allocator模板形参),那么它会让你感觉很好。

当然可以提供你想要的特性,例如,通过使用与allocator模板形参相同的方法,可以有一个typename来指定在"push back"形参中使用的类型。

但它不是那样设计的,因为它看起来很复杂,根本不值得。这是不值得的,因为编译器优化将会避免基本类型的大量开销,因为静态分析在基本类型上运行要容易得多。

其次,通过引用传递int与在方法参数中复制int之间的"损失时间"被认为是平台特异性的,无法在"虚拟机"级别(语言编写者必须将自己置于此级别)进行验证。更不用说这里的"过早优化",它会导致大量的代码膨胀,作为一般目的的合理权衡,它被完全淘汰了。这也是因为在设计时,您会考虑基本类型,如稀有性与可以包含的无限可能类型。

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

ps2/编辑:我想补充一点,正如评论中所说,vector<bool>::const_reference不能作为一个可能做什么的例子,因为这是标准中的一个错误,实际上违反了STL 要求,即容器将..::reference定义为T&。进一步证明这个问题的是vector<bool>::reference(不是const)不是bool,它是一个未指定的类(gcc中的_Bit_reference),它作为一个代理来访问和改变一个位,包装在向量中,它将作为"虚拟"bool值的存储支持,使其看起来像存在的。这些奇怪的东西不能移植到泛型向量,因为它们不符合广泛的STL要求。但它不会使正交方法无效,就像我提到的pass_for_copy_param_type一样,它可以是一个可参数化的新类型定义,并在今天编写"const value_type&"的某些地方(合适)用于替换。但这正是我之前提到的代码膨胀。我敢肯定,这么少的钱不值这么多钱。

改变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]++;

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

我还认为大多数编译器会优化使用const&走了。

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

最新更新