常量对象的向量导致编译错误

46

我在我的代码中声明了以下内容

vector <const A> mylist; 

我得到了以下编译错误 -

new_allocator.h:75: error: `const _Tp* __gnu_cxx::new_allocator<_Tp>::address(const _Tp&) const \[with _Tp = const A]' and `_Tp* __gnu_cxx::new_allocator<_Tp>::address(_Tp&) const [with _Tp = const A]' cannot be overloaded

但如果声明 -

vector <A> mylist;

我的代码已经编译通过。

在这种情况下,const不允许使用吗?

我将我的代码复制在此以供大家参考 -

#include <iostream>
#include <vector>

using namespace std;
class A
{
public:
    A () {cout << "default constructor\n";}
    A (int i): m(i) {cout << "non-default constructor\n";}

private:
    int m;
};

int main (void)
{
    vector<const A> mylist;

    mylist.push_back(1);

    return 0;
}

你使用的是哪个编译器?这对我来说很好用。 - Ravid Goldenberg
1
你希望通过const项目实现什么? - TooTone
@petric - 我已经使用g++ (GCC) 3.4.3编译了它。 - Satabdi
2
可能是Does C++11 allow vector<const T>?的重复问题。 - underscore_d
@underscore_d 使用gcc 3.4.3编译:不支持c++11。 - YSC
2个回答

42

向量中的元素必须是可分配的(或者在更近版本的标准中是可移动的)。const对象是不可分配的,因此尝试将它们存储在向量中将会失败(或者至少可能会失败--代码无效,但编译器可以自由选择接受它,尽管大多数程序员通常更喜欢拒绝无效代码)。

我想对于真正的学究们来说,如果你足够想要,你可以定义一个类型,即使是const也是可分配的,就像这样:

class ugly { 
    mutable int x;
public:
    ugly const &operator=(ugly const &u) const { 
        x = u.x;
        return *this;
    }
};

我相信即使它们被标记为const,你仍然应该能够将此类型的项目存储在一个vector 中。使用VC++进行创建这种向量的快速测试是成功的,但在一些旧编译器(例如g++ 4.8.1)中会失败,但对于比较新的编译器来说是可以行得通的(VC++ 至少从2015年起支持,g++至少从5.4版开始支持,clang++至少从4.0版开始支持--尽管我没有尝试跟踪每个第一个支持它的版本)。

对于当前编译器而言,支持移动const对象的类型可能同样适用。但是,仅仅为了以防万一:这样做允许您修改一个被标记为const的对象,这显然违反了任何合理用户的期望,因此这主要是一个问题,而不是解决方案。


实际上,您可以在不可分配的项目上使用向量。只是您不能使用许多成员函数/算法,例如push_back。 - lip
我们遇到了同样的问题。代码在 VC++(2012)中编译并正常工作,但 g++ 4.8.2 和 clang 3.4 都不喜欢它。 - ChrisWue
1
“向量中的项目必须是可分配的”来自C++03;这个要求在C++11中得到了放宽。(然而,目前还不完全清楚C++11是否允许使用const对象的向量;请参见此处 - M.M
@AlwaysLearning:你想做什么并不是关键点,关键是标准对于你放在向量中的元素所施加的要求。我最初根据 C++03 的答案是要求元素可赋值的。自从 C++11 以来,这方面有所放宽,因此允许移动但不能赋值的类型。但像 const int 这样的东西既不能移动也不能赋值,因此不符合条件。我不会试图在哲学层面上辩论这是好还是坏,但这就是(目前)所需的。 - Jerry Coffin
1
@AlwaysLearning 你是在问为什么有人会想要一个由const元素构成的向量吗?我认为这不是一个特别离奇的情况。在某些情况下,调用函数并说“以一种确保我无法稍后修改它们的方式给我一堆T可能很好,尽管我保留自己将更多内容添加到容器中或在完成后销毁它们的权利”。这就是具有const value_type的非const vector所提供的内容,但由于分配器要求,我们无法拥有它。参见https://dev59.com/-Ww05IYBdhLWcg3w6F4w。 - underscore_d
显示剩余2条评论

5
使用push_back方法存在问题,emplace_back则可以编译通过。另一种选择(取决于你未在此处描述的整个情况)是使用vector<A const&>,如果插入的项在向量之外有生命。向量中的项不需要可分配,但当它们不可分配时,某些成员函数和算法无法使用。
解释: push_back应该首先在向量中默认构造一个A,然后使用复制构造函数分配给定引用。这会破坏你的const限定,因此无法编译通过。 emplace_back使用“完美转发”直接在原地调用实际构造函数。

1
通常是正确的,但 emplace_back 不使用对象的移动构造函数。它使用“完美转发”直接在原地调用实际的构造函数。请参见(例如)http://en.cppreference.com/w/cpp/container/vector/emplace_back。 - Nemo
1
我曾认为容器不能存储引用(至少不是vector)。vector<A const&>无法编译。 - Mike Lui
23
容器无法存储引用。这个怎么会得到7个赞? - underscore_d
@underscore_d:实际上,它已经得到了17个赞同(和10个反对)。 - Jerry Coffin
@JerryCoffin 我不记得 6 年前我是否有能力查看单独计数,所以我可能在谈论聚合结果,或者也许其他人在这 6 年里投票了...不过知道这一点还是很好的 :-) - underscore_d
1
@underscore_d:是的 - 大多数时候,我发现这很有趣(接近惊人),多年后,可见分数仍然没有改变。 - Jerry Coffin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接