代码之家  ›  专栏  ›  技术社区  ›  anon

我可以在向量中使用const来允许添加元素,但不能修改已经添加的元素吗?

  •  60
  • anon  · 技术社区  · 14 年前

    我的评论 this answer 让我想到了关于结构和分类的问题。我玩了一会儿,把我的问题简化成了这样一个事实:

    #include <vector>
    
    int main() {
        std::vector <const int> v;  
    }
    

    不会编译-无法创建常量int的向量。显然,我应该知道这一点(在智力上我也知道),但我以前从来没有必要创造这样的东西。然而,它对我来说似乎是一个有用的构造,我想知道是否有任何方法可以解决这个问题。- 我想把东西添加到一个向量(或其他什么),但是一旦添加它们就不应该改变。

    可能有一些令人尴尬的简单解决方案,但这是我以前从未考虑过的。 我可能不应该提到排序(我可能会问另一个关于排序的问题,见 this 对于提问的困难)。我真正的基本用例是这样的:

    vector <const int> v;     // ok (i.e. I want it to be OK)
    v.push_back( 42 );        // ok
    int n = v[0];             // ok
    v[0] = 1;                 // not allowed
    
    14 回复  |  直到 8 年前
        1
  •  22
  •   avakar    12 年前

    嗯,在C++ 0x中你可以…

    在C++ 03中,有一个段落23.1 [LIB,容器,要求] / 3,它表示

    存储在这些组件中的对象类型必须满足 CopyConstructible 类型(20.1.3),以及 Assignable 类型。

    这是当前阻止您使用的 const int 作为类型参数 std::vector .

    但是,在C++0x中,这个段落丢失了, T 必须是 Destructible 以及其他要求 T 按表达式指定,例如 v = u STD::载体 仅在以下情况下有效 T MoveConstructible MoveAssignable .

    如果我正确地解释了这些需求,应该可以实例化 std::vector<const int> ,您将错过它的一些功能(我想这是您想要的)。您可以通过向构造函数传递一对迭代器来填充它。我想 emplace_back() 应该也可以,尽管我没有找到明确的要求 T 为了它。

    不过,您仍然无法在适当的位置对向量进行排序。

        2
  •  17
  •   Jonathan M Davis    14 年前

    在标准容器中放置的类型必须是可复制和可分配的。原因在于 auto_ptr 造成如此多的麻烦正是因为它不遵循正常的复制和分配语义。当然,任何 const 不会被分配。所以,你不能坚持 康斯特 标准容器中的任何东西。如果元素不是 康斯特 然后你 将能够改变它。

    我认为最接近的解决办法是使用某种间接的方法。所以,您可以有一个指向const的指针,或者您可以有一个对象,它保存了您想要的值,但不能在对象内更改该值(就像 Integer 在Java中)。

    将元素保持在一个特定的索引上是不变的,这与标准容器的工作方式不符。您可能能够构建自己的以这种方式工作的,但是标准的不能。并且,基于数组的任何一个都不能工作,除非您能够设法将它们的初始化放入 {a, b, c} 初始化语法自从一次数组 康斯特 已创建,无法更改。所以,A vector 不管您做什么,类都不可能与const元素一起工作。

    康斯特 在一个没有某种间接性的容器中工作不太好。你基本上要求做整个容器 康斯特 -如果您从已经初始化的容器复制到它,那么您可以这样做,但是您不能真正拥有一个容器(当然不是标准容器),它包含常量而没有某种间接性。

    编辑 :如果您要做的主要是保持容器不变,但仍然能够在代码中的某些位置更改它,那么在大多数位置使用const-ref,然后提供需要能够更改容器直接访问或非const-ref的代码将使这成为可能。

    所以,使用 const vector<int>& 在大多数地方,然后 vector<int>& 如果需要更改容器,或者让代码的这一部分直接访问容器。这样,它基本上是不变的,但是你可以在你想改变的时候改变它。

    另一方面,如果您希望能够经常更改容器中的内容,但不更改特定元素,那么我建议在容器周围放置一个包装类。在情况下 矢量 ,包装它,并使下标运算符返回一个const-ref而不是一个非const-ref-或者返回一个副本。因此,假设您创建了一个模板化的版本,那么您的subscript操作符应该如下所示:

    const T& operator[](size_t i) const
    {
        return _container[i];
    }
    

    这样,您可以更新容器本身,但不能更改它的单个元素。而且,只要您以内联方式声明所有的函数,那么拥有包装器就不会对性能造成太大影响(如果有的话)。

        3
  •  10
  •   cHao Hammerite    14 年前

    你不能创建一个常量向量,即使你可以,它也会非常无用。如果删除第二个int,那么从那里开始的所有内容都会向下移动一个--read:modified--这样就不可能保证v[5]在两种不同的情况下具有相同的值。

    除此之外,一个常量在被声明之后就不能被分配给它,除非将其丢弃。如果你想这样做,你为什么要先用警察?

        4
  •  7
  •   Edward Strange    14 年前

    你需要写自己的课。您当然可以使用std::vector作为内部实现。然后只需实现const接口和您需要的几个非const函数。

        5
  •  4
  •   Thomas Matthews    14 年前

    虽然这并不能满足您的所有要求(能够排序),但是尝试一个常量向量:

    int values[] = {1, 3, 5, 2, 4, 6};
    const std::vector<int> IDs(values, values + sizeof(values));
    

    不过,您可能需要使用 std::list . 对于列表,值不需要更改,只需要链接到它们。排序是通过更改链接的顺序来完成的。

    你可能需要花费一些脑力来写自己的。:

        6
  •  2
  •   Loki Astari    14 年前

    我将把所有常量对象都放在一个标准数组中。
    然后使用指向数组的指针向量。
    一个小的实用程序类只是为了帮助您不必去引用对象和hay presto。

    #include <vector>
    #include <algorithm>
    #include <iterator>
    #include <iostream>
    
    
    class XPointer
    {
        public:
            XPointer(int const& data)
                : m_data(&data)
            {}
    
        operator int const&() const
        {
            return *m_data;
        }
    
        private:
            int const*  m_data;
    
    };
    
    int const               data[]    =  { 15, 17, 22, 100, 3, 4};
    
    std::vector<XPointer>   sorted(data,data+6);
    
    
    int main()
    {
        std::sort(sorted.begin(), sorted.end());
        std::copy(sorted.begin(), sorted.end(), std::ostream_iterator<int>(std::cout, ", "));
        int x   = sorted[1];
    }
    
        7
  •  1
  •   Adrian McCarthy    14 年前

    我和诺亚在一起:用一个类包装向量,该类只公开您想要允许的内容。

    如果不需要动态地向向量添加对象,请考虑 std::tr1::array .

        8
  •  1
  •   philsquared    14 年前

    在这种情况下,如果常量对您很重要,我认为您可能希望一直使用不可变类型。从概念上讲,您将拥有一个固定大小的常量数组const int s.任何时候您需要更改它(例如添加或删除元素,或排序),您都需要复制执行了操作的数组并使用它。 虽然这在函数语言中是很自然的,但在C++中似乎并不完全正确。例如,要获得高效的实现可能很困难,但你不能说你的性能需求是什么。 从性能/定制代码的角度来看,不管您是否认为这条路线值得,我相信这是正确的方法。

    在这之后,通过非常量指针/智能指针保存值可能是最好的(当然也有它自己的开销)。

        9
  •  1
  •   Matthieu M.    14 年前

    我一直在考虑这个问题,你的要求似乎被取消了。

    您不希望向向量中添加不可变的值:

    std::vector<const int> vec = /**/;
    std::vector<const int>::const_iterator first = vec.begin();
    
    std::sort(vec.begin(), vec.end());
    
    assert(*vec.begin() == *first); // false, even though `const int`
    

    您真正想要的是向量以可修改的顺序保存一个值的常量集合,该集合不能用 std::vector<const int> 语法即使有效。

    恐怕这是一项非常具体的任务,需要专门的课程。

        10
  •  1
  •   AnT stands with Russia    14 年前

    确实如此 可转让的 是矢量元素类型和 const int 不可分配。但是,我希望在经过深思熟虑的实现中,只有当代码显式地依赖于赋值时,编译才会失败。为了 std::vector 那将是 insert erase 例如。

    实际上,在许多实现中,即使不使用这些方法,编译也会失败。例如,comeau无法编译plain std::vector<const int> a; 因为相应的专业化 std::allocator 编译失败。它没有立即报告 STD::载体 本身。

    我相信这是一个有效的问题。库提供了实现 STD:分配器 如果类型参数是常量限定的,则假定失败。(我想知道是否可以对 STD:分配器 (了解vs如何编译它也很有意思)再次使用comeau std::vector<const int> 由于同样的原因未能编译 std::allocator<const int> 未能编译,并且,根据规范 STD:分配器 必须 编译失败。

    当然,在任何情况下,任何实现都有权不编译 std::vector<const int> 因为语言规范允许它失败。

        11
  •  0
  •   John Dibling    14 年前

    只使用非专业的 vector 无法完成。排序是通过使用赋值来完成的。因此,同样的代码使这成为可能:

    sort(v.begin(), v.end());

    …也使这成为可能:

    v[1] = 123;

        12
  •  0
  •   Mark Ransom    14 年前

    您可以从std::vector派生一个类const向量,该类重载返回引用的任何方法,并使其返回const引用。要进行排序,请向下转换到std::vector。

        13
  •  0
  •   abir    14 年前

    std::vector 因为 Assignable 要求,因为不能分配常量对象。同样适用于移动分配。这也是我在处理基于向量的映射(如Boost)时经常遇到的问题。 flat_map 或洛基 AssocVector . 因为它有内部实施 std::vector<std::pair<const Key,Value> > . 因此,几乎不可能遵循映射的常量键要求,这对于任何基于节点的映射都是很容易实现的。

    不管怎样,无论 std::vector<const T> 表示矢量应存储 const T 类型化对象,或者它只需要在访问时返回一个不可变的接口。 在这种情况下,实现 std::vector<const> 可能遵循可分配/移动可分配要求,因为它存储类型的对象 T 而不是 康斯特 . 标准typedef和分配器类型需要很少修改以支持标准需求。 vector_map 平面地图 一个人可能需要在 std::pair 接口,因为它首先直接公开成员变量,然后直接公开成员变量。

        14
  •  0
  •   n.caillou    8 年前

    编译失败,因为 push_back() (例如)基本上是

    underlying_array[size()] = passed_value;
    

    其中两个操作数都是 T& . 如果 T const X 那不行。

    拥有const元素在原则上似乎是正确的,但在实践中它是不自然的,而且规范并没有说应该支持它,所以它不存在。至少不在stdlib中(因为那样,它将在vector中)。

    推荐文章