C++ 如何正确地将常量指针从非常量迭代器声明为指针 背景

C++ 如何正确地将常量指针从非常量迭代器声明为指针 背景,c++,c++11,const-correctness,C++,C++11,Const Correctness,我正在实现一个模板过滤迭代器。给定任何类型的开始和结束迭代器,该迭代器将在范围内迭代,并跳过一元谓词返回false的任何元素。当然,我希望这个一元谓词总是有一个const参数,以避免谓词修改支持容器 支持迭代器可以是任何类型和容器的迭代器。它可以是基本类型、指针、引用、类。什么都可以 我遇到了一个问题,无法根据模板参数迭代器声明std::function以获得正确的const声明参数。我提取了一个说明问题的最小代码示例 代码 #包括 #包括 typedef std::向量向量类型; typede

我正在实现一个模板过滤迭代器。给定任何类型的开始和结束迭代器,该迭代器将在范围内迭代,并跳过一元谓词返回false的任何元素。当然,我希望这个一元谓词总是有一个const参数,以避免谓词修改支持容器

支持迭代器可以是任何类型和容器的迭代器。它可以是基本类型、指针、引用、类。什么都可以

我遇到了一个问题,无法根据模板参数迭代器声明
std::function
以获得正确的
const
声明参数。我提取了一个说明问题的最小代码示例

代码
#包括
#包括
typedef std::向量向量类型;
typedef std::函数函数函数类型;
void foo(向量类型&a,函数类型f){
对于(自动it=a.begin();it!=a.end();++it){
f(*it);
}
}
int main(int,char**){
v型vec_;
INTA=3;
int b=4;
v、 向后推(&a);
v、 向后推(&b);
foo(v,[](int*x){*x=0;});
返回0;
}

我希望lamda上会出现编译错误,因为
int*
应该是
const int*
,但GCC 4.8.1和VS2013都允许它,并且很高兴地修改了我认为应该是const的内容。

您的容器存储
int*
s。您的函数接受“对
int*
的常量引用”。这意味着指针指向可变数据。由于您允许修改数据,因此可以很高兴地对其进行修改。注意“恒定度”之间的区别——您不能更改指针指向的对象,但可以修改它们指向的
int
s。因此,要解决此问题,您的函数必须接受“const reference to
const int*
”或
const int*

typedef std::function<void(const int*)> func_type;
typedef std::function func_type;
…或者,如果您希望它更通用(请参阅雅克的答案,了解更通用的解决方案):

#包括
typedef std::向量向量类型;
类型定义
std::add_指针<
标准::添加常数<
删除指针<
向量类型::迭代器::值类型
>::类型
>::类型
>::类型值\u t;
typedef std::函数函数函数类型;

修改指针指向的对象时,指针容器不会被修改:容器拥有指针,而不是指向的对象

但我明白——有时候你想要
const
ness来分配下指针

这是一个模板元编程,它应该采用任何非
常量
指针,并使其成为
常量
,以及其他所有内容尽可能成为
常量。它以递归方式运行,处理引用(r和l值)和指针到指针的引用到指针到指针的指针,无论是否带有
const
volatile
修饰符,几乎在任何地方都可以

template<class T>struct tag{using type=T;};

template<class X>
struct make_very_const:tag<const X> {};
template<class X>
using make_very_const_t=typename make_very_const<X>::type;
// makes code below easier to write (namely, the pointer code):
template<class X>
struct make_very_const<const X>:tag<const make_very_const_t<X>> {};
template<class X>
struct make_very_const<volatile X>:tag<const volatile make_very_const_t<X>> {};
template<class X>
struct make_very_const<const volatile X>:tag<const volatile make_very_const_t<X>> {};
// references:
template<class X>
struct make_very_const<X&>:tag<make_very_const_t<X>&>{};
template<class X>
struct make_very_const<X&&>:tag<make_very_const_t<X>&&>{};
// pointers:
template<class X>
struct make_very_const<X*>:tag<make_very_const_t<X>*const>{};
// std::reference_wrapper:
template<class X>
struct make_very_const<std::reference_wrapper<X>>:tag<std::reference_wrapper<make_very_const_t<X>>const>{};
templatestruct标记{using type=T;};
模板
结构make_very_const:tag{};
模板
使用make_very_const_t=typename make_very_const::type;
//使下面的代码更易于编写(即指针代码):
模板
结构make_very_const:tag{};
模板
结构make_very_const:tag{};
模板
结构make_very_const:tag{};
//参考资料:
模板
结构make_very_const:tag{};
模板
结构make_very_const:tag{};
//指针:
模板
结构make_very_const:tag{};
//标准::参考包装:
模板
结构make_very_const:tag{};

这是非常冗长的。之所以如此冗长,是因为没有简单的方法来匹配“类型修饰符”(指针、常量、volatile、引用等),所以最终必须非常具体和冗长

但它为您在使用时提供了一种干净的方法:

typedef std::vector<int*> vec_type;
typedef std::function<void(make_very_const_t<vec_type::iterator::value_type&>)> func_type;
typedef标准::向量向量类型;
typedef std::函数函数函数类型;
它以一种应该隐式转换为的方式将
const
喷到所有东西上

现在,即使这样也不是完全有效。
std::vector
不会保护指向内部的
int
不被修改。以上依赖于隐式转换为更
const
的能力——因此要实现这一点,我们必须在几乎任意的容器周围编写一个const-forcing包装器,强制元素访问以完成上述转换。这很难


通常,如果您想要一个
int*
对象,其中
const
使指向对象
const
,则需要一个智能指针来强制执行该要求。建议在标准中添加一个这样做的包装器,如果不完全出于此目的。

使用
vec_type::const_iterator
而不是
const-vec_type::iterator
@WojciechFrohmberg,这在我的情况下是不可接受的。我将添加一些背景信息。
func\u type
的参数类型是“const reference to
int*
”,而不是“reference to
const int*
”。如果将调用更改为
foo(v,[](int*&x){x=0;})它会失败。@molbdnilo我怀疑发生了类似的事情。但是,如果用户忘记添加
&
(如果您不阅读文档,这一点就不明显),那么代码将很高兴地修改备份容器。你对如何解决这个问题有什么建议吗?
[](int*x){x=0;}
-你为什么认为这是在修改容器?你的答案正是我需要的。我只是想了解你的模板(我用了不同的技术,但结果是一样的)。@EmilyL
tag
是我的懒惰(我可以用
{using type=blah;}
替换
:tag{}
。其他一切都只是“匹配模式,做任何事情”pattern.
make\u very\u const\t
是一个别名,它消除了
typename
的无稽之谈。这些模式基本上是只添加
const
不正确的情况:排列得不正确
template<class T>struct tag{using type=T;};

template<class X>
struct make_very_const:tag<const X> {};
template<class X>
using make_very_const_t=typename make_very_const<X>::type;
// makes code below easier to write (namely, the pointer code):
template<class X>
struct make_very_const<const X>:tag<const make_very_const_t<X>> {};
template<class X>
struct make_very_const<volatile X>:tag<const volatile make_very_const_t<X>> {};
template<class X>
struct make_very_const<const volatile X>:tag<const volatile make_very_const_t<X>> {};
// references:
template<class X>
struct make_very_const<X&>:tag<make_very_const_t<X>&>{};
template<class X>
struct make_very_const<X&&>:tag<make_very_const_t<X>&&>{};
// pointers:
template<class X>
struct make_very_const<X*>:tag<make_very_const_t<X>*const>{};
// std::reference_wrapper:
template<class X>
struct make_very_const<std::reference_wrapper<X>>:tag<std::reference_wrapper<make_very_const_t<X>>const>{};
typedef std::vector<int*> vec_type;
typedef std::function<void(make_very_const_t<vec_type::iterator::value_type&>)> func_type;