C++:如何处理通用const指针

4
我在一些模板代码中遇到了烦人的const-correctness问题,最终归结为以下观察结果:出于某种原因,对于类STL容器T,const typename T::pointer似乎并不产生常量指针类型,即使T::pointer等同于T::value_type*
下面的示例说明了这个问题。假设您有一个模板函数,它接受一个容器,该容器必须满足STL随机访问容器概念要求。
template <class Container>
void example(Container& c)
{
    const typename Container::pointer p1 = &c[0]; // Error if c is const
    const typename Container::value_type* p2 = &c[0]; 
}

然后,如果我们给这个函数传递一个const容器...
const std::vector<int> vec(10);
example(vec);

我们将const int*转换为int*时出现了无效转换。但是,为什么在这个例子中const typename Container::pointer不同于const int*
请注意,如果我将const typename Container::pointer更改为简单的typename Container::const_pointer,它可以编译通过。然而,据我所知,const_pointer typedef是一个扩展(我没有在C++标准容器要求(23.5,表65)中看到它被提及),因此我不想使用它。
那么,如何从容器T获取通用的、const-correct指针类型呢?(我真的看不出来除了使用boost::mpl::if_以及type_traits来检查容器是否是常量之外还有什么更简洁的方法)
编辑:如果有影响的话,我正在使用gcc 4.3.2进行编译。
5个回答

17

它不能正常工作,是因为你的const并没有应用到你认为的那个地方。例如,如果你有以下代码:

typedef int* IntPtr;

然后

const IntPtr p;

不代表

const int* p;

但实际上代表的是

int* const p;

typedef名称不是宏。一旦类型的“指针性”包装到typedef名称中,就无法再使用它来创建指向const类型的指针了。即没有任何方法可以使用上述IntPtr typedef名称来生成等效的指向const类型的指针。

const int* p;

你需要显式地使用指向类型(就像你在value_type中所做的那样),或检查您的容器是否定义了不同的typedef名称,其中const已经“包含”在内(例如const_pointer或类似的内容)。


2
这可能很难记住。我遇到的最好的提示是从右到左阅读。 "int* const p" 是指“const指向int的指针”,即指针是const而int是可变的。 "const int *p" 是指“指向const int的指针”,即指针是可变的但int是const。 - David Joyner
这个答案(以及其他答案)是不正确的。只需要使用一个模板来移除指针即可。请参考我的答案了解详情。 - darune
@darune:首先,这个答案没有任何“错误”。其次,这个答案是在2009年写的,当时根本没有std::remove_pointer这样的东西。第三,如果您可以直接访问指针类型,则不需要使用std::remove_pointer - AnT stands with Russia
@AnT 我看到的错误部分是“绝对没有办法使用上述IntPtr typedef名称来产生等效的”。std::remove_ptr在2009年的C++中很容易实现,并且已经在boost中使用了很长时间。顺便说一句,这不是责备游戏,而是为其他用户提供服务。 - darune

3
这是:

typename Container::pointer

在我们的情况下,类型为int*。我不知道术语,所以很抱歉,但指针指向一种类型。也就是说,Container::pointer是指向可变T的指针,添加const只会使其成为const指针(而不是指向const的指针),因为Container::pointer已经被定义为指向可变T。

似乎只有const_pointer,可以从类中获取或自己定义:

typedef const typename Container::value_type* const_pointer

会工作。


2

分配器要求(参见20.1.5,表32,“分配器要求”)规定STL容器的分配器必须具有Allocator::const_pointer。所有STL容器(第23节列出的八个容器)都定义了一个Container::const_pointer类型定义,如下:

typedef typename Allocator::const_pointer const_pointer;

正如您在问题中所述,除了这八个之外的容器不需要具有 Container::const_pointer typedef。

你能再澄清一下吗?我明白你在说关于const_pointer作为Allocator要求的一部分(在20.1.5中),但是哪里说容器必须定义一个const_pointer typedef呢?我看到最接近的是20.1.5.4,它说容器实现可以假设Allocator有一个const_pointer typedef,但不一定要求容器本身定义一个公共的const_pointer typedef。 - Charles Salvia
我并不是想暗示STL需要容器定义const_pointer typedef,只是所有的STL容器都定义了const_pointer typedef。对于造成的困惑,我很抱歉;我更新了我的回答,试图让它更清晰明了。 - James McNellis

1
这可能与此处相关(来自SGI STL参考):
[6] 与引用[5]的情况一样,指针类型必须具有与C++指针相同的语义,但不一定是C++指针。然而,“智能指针”与“智能引用”不同是可能的。这是因为用户定义的类型可以定义解引用运算符和指针成员访问运算符operator*和operator->。

然而,在这种情况下,T::pointerT::value_type* 是相同的(即,T::pointer 是一个实际的原始指针),因此要求得到满足。 - Charles Salvia - Charles Salvia

1

使用模板可以实现。关键是先移除指针,然后在类型上应用const,最后再将指针应用于该类型 - 例如使用std::remove_pointer

typedef const std::remove_pointer<Container::pointer>::type* PointerToConst;

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